现在演示如何在模板工程 DE10_NANO_SoC_FB 中设计一个用户自定义IP,并把这个IP 添加到HPS 系统当中,然后为该IP 编写Linux驱动,然后通过Linux应用程序来访问该IP。
之前发布过在DE10-Nano上实现PWM呼吸灯的设计,当时这个模块框图如下:
该模块内部有三个计数器,并且pwm波的频率和占空比都是电路里面设定好的。现在把这个pwm稍作修改,让pwm波的频率值和占空比值从模块外传递进来:
3.点击File——New——Verilog HDL File新建一个pwm.v文件并存放在工程目录下的ip/pwm文件夹下(文件夹自己新建)。修改代码如下:
module pwm( input clk, input rst_n, input [31:0] cnt_freq, input [31:0] cnt_duty, output reg led); reg [31:0] cnt0; wire end_cnt0; always @(posedge clk or negedge rst_n)begin if(!rst_n)begin cnt0 <= 0; end else begin if(end_cnt0)begin cnt0 <= 0;end else begin cnt0 <= cnt0 + 1; end end end assign end_cnt0 = (cnt0==cnt_freq -1); always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin led <= 1; end else if(cnt0==cnt_duty-1)begin led <= 0; end else if(end_cnt0)begin led <= 1; end end endmodule
然后再将该模块封装成带有 Avalon-MM 接口的组件:
它有:
一个clock接口(clk_50),用来同步寄存器的值;
一个reset接口(rst_n),用来复位寄存器的值;
一组 slave 接口(as_read、as_address、as_write、as_writedata、as_readdata),用来读写寄存器;
一个 conduit 接口,用来与顶层LED的引脚进行连接。
该模块内设计了两个寄存器:
cnt_freq寄存器地址是0,用于存储pwm波的频率值;
cnt_duty寄存器地址是1,用于存储pwm波占空比值。
写寄存器
当as_chipselect 信号和 as_write 信号有效且 as_address地址是 0,将 as_writedata 端口上的值存入 cnt_freq寄存器;
当as_chipselect 信号和 as_write 信号有效且 as_address地址是 1,将 as_writedata 端口上的值存入 cnt_duty寄存器;
读寄存器
当as_chipselect 信号有效且as_address地址是 0,将cnt_freq寄存器的值赋给 readdata 端口。
当as_chipselect 信号有效且as_address地址是 1,将cnt_duty寄存器的值赋给 readdata 端口。
av_pwm.v 代码
module av_pwm( input clk, input reset_n, input as_read, input as_address, input as_write, output reg [31:0] as_readdata, input [31:0] as_writedata, output led); reg [31:0]cnt_freq; reg [31:0]cnt_duty; pwm u_pwm( .clk(clk), .rst_n(reset_n), .cnt_freq(cnt_freq), .cnt_duty(cnt_duty), .led(led) ); //写pwm频率寄存器 always@(posedge clk or negedge reset_n) if(!reset_n) begin cnt_freq <= 32'd0; end else if(as_write && (as_address == 0))begin cnt_freq <= as_writedata; end else begin cnt_freq <= cnt_freq; end //写pwm占空比寄存器 always@(posedge clk or negedge reset_n) if(!reset_n) begin cnt_duty <= 32'd0; end else if(as_write && (as_address == 1)) begin cnt_duty <= as_writedata; end else begin cnt_duty <= cnt_duty; end //读寄存器 always@(posedge clk or negedge reset_n) if(!reset_n) as_readdata <= 32'd0; else if(as_read)begin case(as_address) 0:as_readdata <= cnt_freq; 1:as_readdata <= cnt_duty; default:as_readdata <= 32'd0; endcase end endmodule
封装PWM IP
点击Platform Designer 图标打开soc_system.qsys文件(如果提示IP 更新请直接点击Close关闭窗口,暂时不用更新IP):
点击File——New Component,依次填入如下信息:
然后点击Files 选项卡,点击Add File... ,将av_pwm.v 和pwm.v 两个文件添加进去:
这时候Component Editor 会自动识别顶层文件如下:
如果自动识别错误也可以手动设置,即双击对应文件的Attributes一栏,然后选中Top-level File 复选框,然后点击OK 即可。(注意,本案例要设置av_pwm.v文件为顶层文件)
分析综合如果出现语法错误会有Analyzing Synthesis Files Completed窗口提示:
用户需要根据提示将问题一一改正,然后再次点击 Analyze Synthesis Files 直到Analyzing Synthesis Files Completed窗口不再报错以后,点击Close:
当然,可能此时Message窗口还有这些错误提示,暂时不管。( 注意:不要把 message栏中的错误提示误认为是综合分析的错误提示了, message 栏中的错误提示是告诉用户这个组件的某些设置有问题,这些是需要在接下来的步骤中解决的。)
Component Editor 在分析综合源文件后会根据组件端口的信号名进行预分组,这些可能不太正确,需要用户手动调整。用户可在Signals & Interfaces页面中对组件的接口和信号进行手动调整(比如进行端口的分组,信号的编辑、删除、新增)。
设置时钟信号,选中左侧的clk[1]信号,在右侧信号参数窗口修改设置如下:
设置复位信号,选中左侧的reset_n[1]信号,在右侧信号参数窗口修改设置如下:
点击左侧的as,将右侧的Name改为as_slave,Associated Clock设置为clock,Associated Reset设置为reset。(注:完成这一步骤的设置后,下方的Messages中的Error数量由5个变为4个。)
点击左侧的avs_address[1]信号,在右侧信号参数窗口修改设置如下:
点击左侧的avs_read[1]信号,在右侧信号参数窗口修改设置如下:
点击左侧的avs_readdata[32],在右侧信号参数窗口修改设置如下:
点击左侧的avs_write[1],在右侧信号参数窗口修改设置如下:
点击左侧的avs_writedata[8],在右侧信号参数窗口修改设置如下:
点击左侧最下面<<add interface>>选择conduit:
点击端口类型conduit_end,在右侧信号参数窗口修改设置如下:
然后左击选中led[1]信号,左击不放,拖拽led[1]信号到端口类型conduit_end下面,并确保右侧信号参数窗口设置如下(应该是output, 这里的图后期会改):
左击选中端口类型avalon_slave_0,继续右击该端口类型并选择菜单Remove 移除该多余的端口类型:
至此,已经完成了pwm IP的所有设置,点击Block Symbol 选项卡,可以看到完整的端口信息:
点击最下方的Finish...按钮保存(点击Finish之前要再次检查下as_slave的Associated Reset是reset而不是none),此时会弹出Save Changes的提示框,点击Yes, Save进行保存。
保存完成后,Component Editor工具自动退出,返回Platform Designer界面。此时,在IP Catalog下便可以看到我们创建的pwm IP:
接下来将pwm IP 添加到DE10_NANO_SoC_FB 的HPS系统中。
双击pwm,然后点击Finish就给HPS系统添加了一个名为pwm_0的IP 模块:
(1) 右击pwm_0选择Edit...将模块名称改为pwm;
(2)连接pwm模块的clock信号到clk_0模块的clk信号端;
(3)连接pwm模块的reset信号到clk_0模块的clk_reset信号端;
(4)连接pwm模块的as_slave信号到mm_bridge_0模块的m0信号端;
(5)双击Export栏的Double-click导出端口,并重命名为led;
(6)修改pwm 模块相对于h2f_lw_axi_master总线的及地址0x0000_6000(pwm 模块挂在h2f_lw_axi_master总线)。
参考DE10_NANO_SoC_FB 的顶层文件DE10_NANO_SoC_FB.v可知 led_pio模块是绑定到FPGA 端的LED[7:1],LED[0] 也被占用,所以这里我们可以先删掉led_pio模块,具体做法是取消掉Platfom Designer 窗口led_pio模块前面的复选框即可:
然后 点击Generate HDL...——Generate——Close——Close——Finish:(这个过程会比较久)
点击OK:
点开Project——Add/Remove Files in Project...
检查soc_system.qip文件已经存在(模板工程之前已经是添加过了的):
在顶层文件DE10_NANO_SoC_FB.v做如下修改并保存:
(1)设置stm_hw_events信号位宽为[20:0]
(2)注释掉语句 assign LED[7:1] = fpga_led_internal;
(3)在语句assign stm_hw_events = {{15{1'b0}}, SW, fpga_debounced_buttons};中去掉fpga_led_internal
(4)打开DE10_NANO_SoC_FB工程的soc_system文件夹下的soc_system_inst.v文件找到新增的led_writeresponsevalid_n 端口并复制:
(6)增加语句 .led_wire ( LED[1] ),
(7)注销掉语句.led_pio_external_connection_export ( fpga_led_internal ),
保存完DE10_NANO_SoC_FB.v,点击如下按钮进行工程全编译:
成功生成sof文件: