本帖最后由 Milinker_XU 于 2016-1-19 09:19 编辑
前面章节中我们利用xilinx的IP做了大量的实验,相比大家对MB这个SOC系统已经有所了解。但有时候想,如果自己实现的功能模块如何添加到MB系统中去呢。这就得对MB的AXI4协议有更深的了解才行了。本章节我们使用MB系统中的通用寄存器,设计个定时器。暂定为使用通用寄存器0吧。 ◎首先,确定好32位通用寄存器0各位的含义,这里我们列出了张表,如下所示。
◎开始设计我们所需要的IP,打开xilinx的EDK开发环境,选择菜单Hardware下的Create or Import Peripheral…。
◎PeripheralFlow中保持默认,创建新的。
◎一直到Name andVersion对话框,将IP命名为soc_timer。IP版本号,保持默认即可。
◎在BusInterface中,将IP挂在AXI4-Lite总线下。
◎在IPIFservices中,选择所有功能。
◎User S/W Register下选择软件可访问的寄存器个数。由于定时器我们就用到了一个通用寄存器,故保持默认值为1即可。
◎在IP Interconnet中,保持默认。这些信号是IP挂在总线内部的信号,其实不需要我们处理的,xilinx开发工具已经帮我们处理好。
◎PeripheralSimulation Support中保持默认,不产生BFM仿真。 ◎下面的窗口中,将其全部选中。
◎继续NEXT,直到出现Finish。 ◎进行向导完成后,打开生成的ip。文件目录:…\mis603_soc\pcores\soc_timer_v1_00_a\devl\projnav。
◎打开soc_timer.xise工程。可以看到刚刚生成的IP源代码。其中user_logic.v中,是用户文件,需要进行相应的修改。
◎让我们打开user_logic.v文件。分析该文件,可以看到,其端口包含刚刚生成的内部信号。如用户外接端口,也可以在里面进行定义,由于我们使用的是定时器,不需要外接接口,因此保持端口名不变。
◎程序继续往下看,知道遇见USERlogic implementation added here。将功能代码,添加进去。我们产生三种定时器US,MS,S,因此所需添加的代码如下。注意主频100Mhz,也就是一个周期10ns。 parameter T1US = 6'd99; parameter T1MS = 16'd99999; parameter T1S = 26'd99999999; reg [5 :0]us_cnt; reg [15:0]ms_cnt; reg [25:0]s_cnt; /*****1us, 1ms ,1s 时钟发生器*******/ always @(posedge Bus2IP_Clk) begin if(us_cnt <= T1US) us_cnt <= us_cnt + 1'b1; else us_cnt <= 0; if(ms_cnt <= T1MS) ms_cnt <= ms_cnt + 1'b1; else ms_cnt <= 0; if(s_cnt <= T1S) s_cnt <= s_cnt + 1'b1; else s_cnt <= 0; end /* Tus_set --1us 定时器定时时间 最大65535 us Tms_set --1ms 定时器定时时间 最大65535 ms Ts_set --1s 定时器定时时间 最大65535 s Tus_up -- 1us 触发信号 Tms_up -- 1ms 触发信号 Ts_up -- 1s 触发信号 Tus_busy --1 us 定时器忙 Tms_busy --1 ms 定时器忙 Ts_busy --1 s 定时器忙 T_Ctr_Reg -- 定时器控制寄存器 */ reg [15:0]Tus_set; reg [15:0]Tms_set; reg [15:0]Ts_set; wire Tus_busy ,Tms_busy,Ts_busy; wire Tus_up,Tms_up,Ts_up; assign Tus_up = (us_cnt==0); assign Tms_up = (ms_cnt==0); assign Ts_up = (s_cnt ==0); assign Tus_busy = (Tus_set>0); assign Tms_busy = (Tms_set>0); assign Ts_busy = (Ts_set >0); wire [31:0] T_Ctr_Reg; assign T_Ctr_Reg= slv_reg0; always @(posedge Bus2IP_Clk) begin if ( Bus2IP_Resetn == 1'b0 ) begin Tus_set <= 0; Tms_set <= 0; Ts_set <= 0; end else begin if(Tus_up && Tus_set) Tus_set <= Tus_set - 1'b1; if(Tms_up && Tms_set) Tms_set <= Tms_set - 1'b1; if(Ts_up && Ts_set ) Ts_set <= Ts_set - 1'b1; if(T_Ctr_Reg&32'h01000000) Tus_set <= T_Ctr_Reg&32'h00ffffff; // 设置1us 定时器时间 单位 us if(T_Ctr_Reg&32'h02000000) Tms_set <= T_Ctr_Reg&32'h00ffffff; // 设置1ms 定时器时间 单位 ms if(T_Ctr_Reg&32'h04000000) Ts_set <= T_Ctr_Reg&32'h00ffffff; // 设置1s 定时器时间 单位 s end end |
◎从上述代码可以看到,只需设置us,ms,s的定时值,启动对应的定时器,即可完成定时。 ◎继续查看下面的程序,修改下面的代码。通知MB系统,此时定时器忙碌。 // implement slave model register read mux always @( slv_reg_read_sel or slv_reg0 ) begin case ( slv_reg_read_sel ) 1'b1 : slv_ip2bus_data <= {Tus_busy ,Tms_busy,Ts_busy}; default : slv_ip2bus_data <= 0; endcase end // SLAVE_REG_READ_PROC |
◎上述操作完成后,编译代码,没有错误后,关闭soc_timer工程。从EDK中,可以看到USER中的SOC_TIMER IP。
◎添加完成后,可以看到该IP,已经挂在AXI4Lite_0下面了。跟xilinx提供的ip一模一样嘛。
◎运行Hardware下的Generate NetList产生网表。 ◎返回至ISE工程下,运行工程产生bit文件。 ◎返回XPS平台,进入SDK开发平台。在system.xml中查看ip版本号是否都有,否则,重启SDK。
◎新建一个工程,工程命名为Stimer,以helloworld作为模板。 ◎将Stimer工程中的helloworld.c文件修改成Stimer.c文件。再新建两个文件,一个Stimer.h的头文件,一个timer_set.c文件。
◎在timer_set.c文件中,完成定时器初值的设定以及定时器启动,对应的函数介绍如下所示。 delay_us() - 微秒精度定时器,最大24b 长度 需要等待时间到才返回 delay_ms() - 毫秒精度定时器,最大24b 长度 需要等待时间到才返回 delay_s() - 秒精度定时器,最大24b 长度 需要等待时间到才返回 delay_us_loop() - 微秒精度定时器,最大24b 长度 立刻返回 1代表定时时到 0代表定时未到 delay_ms_loop() - 毫秒精度定时器,最大24b 长度 立刻返回 1代表定时时到 0代表定时未到 delay_s_loop() - 秒精度定时器,最大24b 长度 立刻返回 1代表定时时到 0代表定时未到 其代码如下所示:
/* * timer_set.c * * Created on: 2016-1-18 * Author: Administrator */ #include "Stimer.h" void delay_us( u16 val) { En_Timer(0x01000000|val); while(STIMER_STATU&0x01); } void delay_ms( u16 val) { En_Timer(0x02000000|val); while(STIMER_STATU&0x02); } void delay_s( u16 val) { En_Timer(0x04000000|val); while(STIMER_STATU&0x04); } u8 us_s=0; u8 delay_us_loop( u16 val ) // loop 调用后直接返回 { switch(us_s) { case 0: En_Timer(0x01000000|val);//启动定时器 us_s=1; break; case 1: if(STIMER_STATU&0x01);else us_s=0; break; } if(us_s) return 0; else return 1; } u8 ms_s=0; u8 delay_ms_loop( u16 val ) // loop 调用后直接返回 { switch(ms_s) { case 0: En_Timer(0x02000000|val);//启动定时器 ms_s=1; break; case 1: if(STIMER_STATU&0x02);else ms_s=0; break; } if(ms_s) return 0; else return 1; } u8 s_s=0; u8 delay_s_loop( u16 val ) // loop 调用后直接返回 { switch(s_s) { case 0: En_Timer(0x04000000|val);//启动定时器 s_s=1; break; case 1: if(STIMER_STATU&0x04);else s_s=0; break; } if(s_s) return 0; else return 1; } |
◎Stimer.h文件中,同样只包含必要的头文件和函数声明。 /* * Stimer.h * * Created on: 2016-1-18 * Author: Administrator */ #ifndef STIMER_H_ #define STIMER_H_ #include <stdio.h> #include "platform.h" #include "xparameters.h" #include "xbasic_types.h" #include "xil_io.h" #include "xgpio.h" //寄存器操作 #define En_Timer(x) {Xil_Out32(XPAR_SOC_TIMER_0_BASEADDR,x);Xil_Out32(XPAR_SOC_TIMER_0_BASEADDR,0);} #define STIMER_STATU Xil_In32 (XPAR_SOC_TIMER_0_BASEADDR) void delay_us (u16 val); void delay_ms (u16 val); void delay_s (u16 val); u8 delay_us_loop (u16 val); u8 delay_ms_loop (u16 val); u8 delay_s_loop (u16 val); #endif /* STIMER_H_ */ |
◎主函数中将LED用来做定时器的测试。 #include "Stimer.h" int main() { u8 i=0; u32 led; led=0x00000000; XGpio_Out32(XPAR_LED_BASEADDR,led); delay_ms(1000); led=0xffffffff; XGpio_Out32(XPAR_LED_BASEADDR,led); delay_ms(1000); led=0x55555555; XGpio_Out32(XPAR_LED_BASEADDR,led); delay_ms(1000); while(1) { i=0; led=0x000000FE; while(i<8) { XGpio_Out32(XPAR_LED_BASEADDR,led); led<<=1; i++; delay_ms(1000); } } return 0; } |
◎实际上,受制于MB系统自身的工作效率,上面的定时不是很准确的。大家若是实际工作中遇到需要精确定时的,还是使用xilinx自带的IP,这里只是给大家如何自己添加个IP提供指导。
|