Está en la página 1de 12

Hardware Design

Refer previous experiments for the following steps.

Step 1. Invoke the Vivado IDE and Create a project

Step 2. Create an IP Integrator Design

Step 3. Add and setup the Zynq processor system IP block

Step 4. Customize the Zynq block for our design

Step 5. Add the soft peripherals

Step 6. Generate HDL Design Files

Step 7. Synthesis, Implement and Generate Bitstream

Software Design

The goal of the software portion of this tutorial is to create a basic C/OS-III project. Then
demonstrate interfacing with the AXI Timer using both the Micrium custom driver and Xilinx
standalone driver.

Step 1. Installation of the C/OS Repository

Full installation instructions are available in the user manual.

1. To install the repository add it to the current workspace from the Xilinx Tools->Repositories menu.
Figure - Xilinx SDK Repository Preferences

Step 2. Generate the C/OS BSP

The first step is to generate the C/OS BSP for the hardware platform and a simple C/OS "Hello
World" type project.

1. Open the Xilinx SDK. This should have been done as the last step of the Hardware Design section.

Figure - Xilinx SDK Main Screen

2. Open the "New Application Project Dialog". It can be reached from the File->New->Application
Project menu.

In this dialog, enter the project name and select "ucos" as the OS Platform. Click Next.
Figure - New Application Project Dialog

3. The New Project Template dialog should appear next. Select uC/OS-III Hello World then click
Finish.
Figure - New Project Template Dialog

Now we can see the Board Support Package summary in the IDE.

4. Open the Board Support Package Settings dialog by clicking "Modify this BSP's Settings".

5. Select the necessary libraries ucos_common, ucos_osiii and ucos_standalone.

The ucos_common library is always required by the BSP as well as one of the kernel either ucos_osii
or ucos_osiii but not both. The ucos_standalone package is a compatibility component enabling the
use of Xilinx standalone drivers. Details on the library components can be found in the Supported
Micrium Products section of the user manual with links to the documentation.
Figure - BSP Settings Overview

6. Select the STDOUT provider in the "ucos" configuration section.For the ZC702 and ZedBoard this
should be ps7_uart_1.

Figure - BSP STDOUT Setting

7. Configure the drivers for the AXI Timers. In this tutorial axi_timer_0 will be programmed with the
C/OS custom driver while axi_timer_1 will use the Xilinx standalone driver.
Figure - BSP Drivers Configuration

8. Click OK.

Step 3. Build and Debug the Demonstration Project

The default project generated is a simple Hello World message printed from the main task.

1. Build the project. This is usually done automatically by the Xilinx SDK after modifying the BSP
configuration.

2. Select the project (Not the BSP) in the workspace and open the debug configuration dialog from
the Run->Debug Configurations... menu.

3. Create a new debug configuration by double clicking "Xilinx C/C++ Application (System
Debugger)".

4. Check the "Reset Entire System" and "Program FPGA" in the newly created debug configuration.
This will automatically program the FPGA when starting a debug session.
Figure - Debug Configuration

5. Click Debug.

After programming the FPGA the debugger should now be stopped at the main () function.

6. Connect a terminal to the COM port of you development board. The embedded terminal or any
other terminal app can be used.

Figure - Terminal Configuration

7. Run the demonstration by pressing Run->Resume or F8. You should see text output in the
terminal.

Figure - Terminal Output

Step 4. Program the AXI Timer 0 with the ucos_axitimer Driver

For some peripherals, Micrium distributes custom drivers which are usually designed to be thread
safe for use by RTOS services. For example, the ucos_axitimer primary role is to act as a driver for
the kernel timebase of MicroBlaze systems. These drivers are made available for general usage for
convenience.
In this step you will create a new kernel task waiting on a semaphore periodically posted by an
interrupt service routine. This ISR will be triggered by the AXI Timer 0 using the ucos_axitimer. In
step 5 the same manipulation will be done using the Xilinx standalone driver.

For clarity the error codes returned by the various kernel functions are not checked in the following
examples. Errors should usually be validated in a final application.

1. Create a new task and semaphore.

The first step is to declare the task function, it's TCB (Task Control Block) and stack space near the
top of app.c. At the same time we need a single semaphore named Timer0Semaphore in this
example.

Listing - Timer 0 Task Declarations

A bare task in C/OS-III is a simple function, Timer0Task in this example. To help the demo we can
add a UCOS_Print() at the start of the new task to ensure it was created successfully. See

Listing - Timer 0 Task Skeleton Since we do not want this task to return a while(1) is added near the
end of the function.

Listing - Timer 0 Task Skeleton

The UCOS_Print() implementation is re-entrant (Thread safe) which means it can be called from
multiple tasks without special synchronization.

A semaphore is used in this example to await the signal from the timer. Creating a semaphore in
C/OS-III is a simple function call as illustrated in

OSSemCreate(&Timer0Semaphore, "Timer 0 Semaphore", 0, &os_err);

Listing - Timer 0 Semaphore Create


The Timer 0 task can pend (Wait) on this semaphore once it's created with the OSSemPend() and
output something on the terminal when it is signaled. Finally the task should be created with the
OSTaskCreate() function call.

The following task shows the current content of the main and Timer0 tasks.

1 void MainTask (void *p_arg)


2 {
3 OS_ERR os_err;
4 UCOS_Print("Hello world from the main task\r\n");
5 OSSemCreate(&Timer0Semaphore, "Timer 0 Semaphore", 0,
6 &os_err);
7 OSTaskCreate(&Timer0TCB,
8 "Timer 0 Task",
9 Timer0Task,
10 DEF_NULL,
11 10,
12 Timer0TaskStk,
13 0,
14 512,
15 0,
16 0,
17 DEF_NULL,
18 0,
19 &os_err);
20
21 while (DEF_TRUE) {
22 OSTimeDlyHMSM(0, 0, 10, 0, OS_OPT_TIME_HMSM_STRICT,
23 &os_err);
24 UCOS_Print("Periodic output every 10 seconds from the main
25 task\r\n");
26 }
27 }
28
29 void Timer0Task (void *p_arg)
30 {
31 OS_ERR os_err;
32
33 UCOS_Print("Timer0Task reached\r\n");
34
35 while (1) {
36 OSSemPend(&Timer0Semaphore, 0, 0, DEF_NULL, &os_err);
37 UCOS_Print("Timer 0 Semaphore signaled\r\n");
38 }
39 }

Running the program now will show the Timer 0 starting but pending indefinitely on the semaphore
since the timer isn't configured yet.

2. Configure the AXI Timer 0 to signal the Timer 0 semaphore.


Micrium custom drivers usually register a default interrupt handler when initialized. The interrupt
source is deducted from the hardware design. In the case of the ucos_axitimer driver it's possible to
register a callback to be invoked when the interrupt trigger. The function used is shown in

1 void Timer0ISR (AXITIMER_HANDLE handle, CPU_INT32U tmr_nbr)


2{
3 OS_ERR os_err;
4 OSSemPost(&Timer0Semaphore, 0, &os_err);
5}

Timer 0 ISR

The ISR simply post the Timer 0 semaphore.

The last step is to configure the AXI Timer. The public api of the driver can be accessed by including
the ucos_axitimer.h header file to app.c. Micrium drivers are centered around handles which are
returned by the various Init functions.

The following command line shows the declaration of an AXI Timer handle for Timer 0.

1 AXITIMER_HANDLE Timer0;

Timer 0 Handle Declaration

To configure the timer it must first be initialized, then configured as a countdown, auto-reload timer
with interrupts enabled. In the hardware design the timer is driven by a 50MHz clock and we'll use a
load value of 100 Million to give a 2 seconds delay between interrupts.

Timer0 = AXITimer_Init(0);
1
AXITimer_OptSet(Timer0, 0, AXITIMER_OPT_DOWN |
2
AXITIMER_OPT_AUTO_RELOAD | AXITIMER_OPT_INT);
3
AXITimer_LoadSet(Timer0, 0, 100000000);
4
AXITimer_CallbackSet(Timer0, 0, Timer0ISR);

Timer 0 Setup

Finally the timer can be started.

1 AXITimer_Start(Timer0, 0);

Timer 0 Start

3. Run the application. The output should look like


Figure - Timer 0 Terminal Output

Step 5. Program the AXI Timer 1 with the Xilinx tmrctr Driver

In step 4 the Micrium custom driver ucos_axitimer was used to generate a periodic interrupt waking
an application task. The same can be accomplished by using the Xilinx standalone driver distributed
with the SDK.

When using a standalone driver it's important to have the ucos_standalone library included in the
project. Moreover, if the peripheral is used from multiple threads the necessary synchronization
must be provided by the application, either by using kernel semaphores or mutexes.

1. Create a new task and semaphore similar to item 1 of step 4.

2. Write a custom interrupt service routine for the timer.

1
void Timer1ISR (void *p_arg, CPU_INT32U cpu)
2
{
3
CPU_INT32U ControlStatusReg;
4
OS_ERR os_err;
5
ControlStatusReg = XTmrCtr_ReadReg(Timer1.BaseAddress,
6
0,
7
XTC_TCSR_OFFSET);
8
9
XTmrCtr_WriteReg(Timer1.BaseAddress,
10
0,
11
XTC_TCSR_OFFSET,
12
ControlStatusReg |
13
XTC_CSR_INT_OCCURED_MASK);
14
15
OSSemPost(&Timer1Semaphore, 0, &os_err);
16
}
17

Timer 1 ISR

Raw interrupt routines under the C/OS all have the same signature, part of which are only relevant
in some contexts. The p_arg argument is a user specified parameter given when registering the
interrupt. The cpu argument is the core id of the cpu that generated the interrupt and is only
relevent for software generated interrupts on the cortex-A9. The cpu argument will be 0 in all other
cases.

3. Register and enable the custom interrupt.


1
UCOS_IntVectSet(62, 0, DEF_BIT_00, Timer1ISR, &Timer1);
2
UCOS_IntSrcEn(62);
3

Timer 1 Interrupt Configuration

4. Build and Run. The output should be similar to the previous output from step 4.

También podría gustarte