zoukankan      html  css  js  c++  java
  • ZYNQ自定义AXI总线IP应用——PWM实现呼吸灯效果

    一、前言

      在实时性要求较高的场合中,CPU软件执行的方式显然不能满足需求,这时需要硬件逻辑实现部分功能。要想使自定义IP核被CPU访问,就必须带有总线接口。ZYNQ采用AXI BUS实现PS和PL之间的数据交互。本文以PWM为例设计了自定义AXI总线IP,来演示如何灵活运用ARM+FPGA的架构。

    功能定义:在上一篇ZYNQ入门实例博文讲解的系统中添加自定义IP核,其输出驱动LED等实现呼吸灯效果。并且软件通过配置寄存器方式对其进行使能、打开/关闭配置以及选择占空比变化步长。另外,可以按键操作完成占空比变化步长的增减。

    平台:米联客 MIZ702N (ZYNQ-7020)

    软件:VIVADO+SDK 2017

    注:自定义IP逻辑设计采用明德扬至简设计法

    二、PWM IP设计

      PWM无非就是通过控制周期脉冲信号的占空比,也就是改变高电平在一段固定周期内的持续时间来达到控制目的。脉冲周期需要一个计数器来定时,占空比由低变高和由高变低两种模式同样需要一个计数器来指示,因此这里使用两个嵌套的计数器cnt_cyc和cnt_mode。cnt_mode的加一条件除了要等待cnt_cyc计数完成,还要考虑占空比的变化。

      我们可以使用下降沿位置表示占空比,位置越靠近周期值占空比越高。在模式0中下降沿位置按照步长增大直至大于等于周期值,模式1中下降沿位置则按照步长递减直到小于步长。使用两个信号up_stage和down_stage分别指示模式0和模式1。至于步长值,在配置有效时被更新,否则使用默认值。模块最终的输出信号在周期计数器小于下降沿位置为1,反之为零。设计完毕,上代码:

      1 `timescale 1ns / 1ps
      2 //////////////////////////////////////////////////////////////////////////////////
      3 // Company: 
      4 // Engineer: 
      5 // 
      6 // Create Date: 2020/03/01 18:14:44
      7 // Design Name: 
      8 // Module Name: pwm
      9 // Project Name: 
     10 // Target Devices: 
     11 // Tool Versions: 
     12 // Description: 
     13 // 
     14 // Dependencies: 
     15 // 
     16 // Revision:
     17 // Revision 0.01 - File Created
     18 // Additional Comments:
     19 // 
     20 //////////////////////////////////////////////////////////////////////////////////
     21 
     22 
     23 module pwm(
     24 input                       clk,//频率100MHz 10ns
     25 input                       rst_n,
     26 input                       sw_en,//输出使能
     27 input                       sw_set_en,//步长设定使能
     28 input       [10-1:0]        sw_freq_step,//步长数值
     29 output reg                  led
     30     );
     31 
     32 parameter FREQ_STEP = 10'd100;
     33 
     34 parameter CNT_CYC_MAX = 50000;
     35 
     36 function integer clogb2 (input integer bit_depth);
     37     begin
     38         for(clogb2=0;bit_depth>0;clogb2=clogb2+1)
     39             bit_depth = bit_depth >> 1;
     40     end
     41 endfunction
     42 
     43 localparam CNT_CYC_WIDTH = clogb2(CNT_CYC_MAX-1);
     44            
     45 
     46 reg [CNT_CYC_WIDTH-1:0] cnt_cyc=0;
     47 wire add_cnt_cyc,end_cnt_cyc;
     48 reg [2-1:0] cnt_mode=0;
     49 wire add_cnt_mode,end_cnt_mode;
     50 wire up_stage,down_stage;
     51 reg [CNT_CYC_WIDTH+1-1:0] neg_loc=0;
     52 reg [10-1:0] freq_step=FREQ_STEP;
     53 
     54 
     55 //周期计数器 计数50ms=50*1000ns = 50000_0ns
     56 always@(posedge clk)begin
     57     if(~rst_n)begin
     58         cnt_cyc <= 0;
     59     end
     60     else if(add_cnt_cyc)begin
     61         if(end_cnt_cyc)
     62             cnt_cyc <= 0;
     63         else
     64             cnt_cyc <= cnt_cyc + 1'b1;
     65     end
     66 end
     67 
     68 assign add_cnt_cyc = sw_en == 1;
     69 assign end_cnt_cyc = add_cnt_cyc && cnt_cyc == CNT_CYC_MAX- 1;
     70 
     71 //模式计数器 0-占空比递增 1-占空比递减
     72 always@(posedge clk)begin
     73     if(~rst_n)begin
     74         cnt_mode <= 0;
     75     end
     76     else if(add_cnt_mode)begin
     77         if(end_cnt_mode)
     78             cnt_mode <= 0;
     79         else
     80             cnt_mode <= cnt_mode + 1'b1;
     81     end
     82 end
     83 
     84 assign add_cnt_mode = end_cnt_cyc && ((up_stage && neg_loc >= CNT_CYC_MAX) || (down_stage && neg_loc == 0));
     85 assign end_cnt_mode = add_cnt_mode && cnt_mode == 2 - 1;
     86 
     87 
     88 //变化步长设定
     89 always@(posedge clk)begin
     90     if(~rst_n)begin
     91         freq_step <= FREQ_STEP;
     92     end
     93     else if(sw_set_en)begin
     94         if(sw_freq_step >= 1 && sw_freq_step < 2000)
     95             freq_step <= sw_freq_step;
     96         else
     97             freq_step <= FREQ_STEP;
     98     end
     99 end
    100 
    101 //脉冲下降沿对应周期计数器数值
    102 always@(posedge clk)begin
    103     if(~rst_n)begin
    104         neg_loc <= 0; 
    105     end
    106     else if(end_cnt_cyc)begin
    107         if(up_stage )begin//占空比递增阶段
    108             if(neg_loc < CNT_CYC_MAX)
    109                 neg_loc <= neg_loc + freq_step;
    110         end
    111         else if(down_stage )begin//占空比递减阶段
    112             if(neg_loc < freq_step)
    113                 neg_loc <= 0;
    114             else
    115                 neg_loc <= neg_loc - freq_step;
    116         end
    117     end
    118 
    119 end
    120 
    121 assign up_stage = add_cnt_cyc && cnt_mode == 0;
    122 assign down_stage = add_cnt_cyc && cnt_mode == 1;
    123 
    124 
    125 //输出
    126 always@(posedge clk)begin
    127     if(~rst_n)begin
    128         led <= 1'b0;//高电平点亮
    129     end
    130     else if(add_cnt_cyc && cnt_cyc < neg_loc)begin
    131         led <= 1'b1;
    132     end
    133     else
    134         led <= 1'b0;
    135 end
    136 
    137 
    138 
    139 endmodule
    pwm.v

      VIVADO综合、布局布线比较慢,且软硬件级联调试费时费力,所以仿真是极其重要的。编写一个简单的testbench,定义update_freq_step task更新步长。这里使用System Verilog语法有一定的好处。首先单驱动信号可以统一定义为logic变量类型,其次等待时长能指定单位。

     1 `timescale 1ns / 1ps
     2 //////////////////////////////////////////////////////////////////////////////////
     3 // Company: 
     4 // Engineer: 
     5 // 
     6 // Create Date: 2020/03/01 20:49:25
     7 // Design Name: 
     8 // Module Name: testbench
     9 // Project Name: 
    10 // Target Devices: 
    11 // Tool Versions: 
    12 // Description: 
    13 // 
    14 // Dependencies: 
    15 // 
    16 // Revision:
    17 // Revision 0.01 - File Created
    18 // Additional Comments:
    19 // 
    20 //////////////////////////////////////////////////////////////////////////////////
    21 
    22 
    23 module testbench();
    24 
    25 logic clk,rst_n;
    26 logic sw_en,sw_set_en;
    27 logic [10-1:0]sw_freq_step;
    28 logic led;
    29 
    30 parameter CYC = 10,
    31           RST_TIM = 2;
    32 
    33 defparam dut.CNT_CYC_MAX = 2000;
    34 
    35  pwm#(.FREQ_STEP(100))
    36  dut(
    37 .clk           (clk) ,//频率100MHz 10ns
    38 .rst_n         (rst_n) ,
    39 .sw_en         (sw_en) ,//输出使能
    40 .sw_set_en     (sw_set_en) ,//步长设定使能
    41 .sw_freq_step  (sw_freq_step) ,//步长数值
    42 .led           (led)
    43     );
    44 
    45 initial begin
    46     clk = 1;
    47     forever begin
    48         #(CYC/2.0);
    49         clk=~clk;
    50     end
    51 end
    52 
    53 initial begin
    54     rst_n = 1;
    55     #1;
    56     rst_n = 0;
    57     #(RST_TIM*CYC) rst_n = 1;
    58 end
    59 
    60 initial begin
    61     sw_en = 0;
    62     sw_set_en = 0;
    63     sw_freq_step = 'd10;
    64     #1;
    65     #(RST_TIM*CYC);
    66     #(CYC*10);
    67     sw_en = 1;
    68 
    69     #600us;
    70     update_freq_step(50);
    71     #600us;
    72     $stop;
    73 
    74 end
    75 
    76 task update_freq_step([10-1:0] freq_step);
    77     sw_set_en = 1;
    78     sw_freq_step = freq_step;
    79     #(1*CYC);
    80     sw_set_en = 0;
    81 endtask
    82 
    83 endmodule
    testbench.sv

      设计较简单,直接使用VIVADO仿真器观察波形即可:

       可以看到输出信号led的占空比在不断起伏变化,当更新freq_step为50后变化更为减慢。

       配置前相邻两个neg_loc数值差与更新后分别是100和50。以上证明逻辑功能无误。

    三、硬件系统搭建

       设计完PWM功能模块还没有完,需要再包一层总线Wrapper才能被CPU访问创建AXI总线IP

      在封装器中编辑。

      最终IP结构如图:

       具体操作不过多讲述,直接以代码呈现:

     1 `timescale 1 ns / 1 ps
     2 
     3     module pwm_led_ip_v1_0 #
     4     (
     5         // Users to add parameters here
     6         parameter FREQ_STEP = 10'd100,
     7         // User parameters ends
     8         // Do not modify the parameters beyond this line
     9 
    10 
    11         // Parameters of Axi Slave Bus Interface S00_AXI
    12         parameter integer C_S00_AXI_DATA_WIDTH    = 32,
    13         parameter integer C_S00_AXI_ADDR_WIDTH    = 4
    14     )
    15     (
    16         // Users to add ports here
    17         output led,
    18         // User ports ends
    19         // Do not modify the ports beyond this line
    20 
    21 
    22         // Ports of Axi Slave Bus Interface S00_AXI
    23         input wire  s00_axi_aclk,
    24         input wire  s00_axi_aresetn,
    25         input wire [C_S00_AXI_ADDR_WIDTH-1 : 0] s00_axi_awaddr,
    26         input wire [2 : 0] s00_axi_awprot,
    27         input wire  s00_axi_awvalid,
    28         output wire  s00_axi_awready,
    29         input wire [C_S00_AXI_DATA_WIDTH-1 : 0] s00_axi_wdata,
    30         input wire [(C_S00_AXI_DATA_WIDTH/8)-1 : 0] s00_axi_wstrb,
    31         input wire  s00_axi_wvalid,
    32         output wire  s00_axi_wready,
    33         output wire [1 : 0] s00_axi_bresp,
    34         output wire  s00_axi_bvalid,
    35         input wire  s00_axi_bready,
    36         input wire [C_S00_AXI_ADDR_WIDTH-1 : 0] s00_axi_araddr,
    37         input wire [2 : 0] s00_axi_arprot,
    38         input wire  s00_axi_arvalid,
    39         output wire  s00_axi_arready,
    40         output wire [C_S00_AXI_DATA_WIDTH-1 : 0] s00_axi_rdata,
    41         output wire [1 : 0] s00_axi_rresp,
    42         output wire  s00_axi_rvalid,
    43         input wire  s00_axi_rready
    44     );
    45 // Instantiation of Axi Bus Interface S00_AXI
    46     pwd_led_ip_v1_0_S00_AXI # ( 
    47         .C_S_AXI_DATA_WIDTH(C_S00_AXI_DATA_WIDTH),
    48         .C_S_AXI_ADDR_WIDTH(C_S00_AXI_ADDR_WIDTH),
    49         .FREQ_STEP(FREQ_STEP)
    50     ) pwd_led_ip_v1_0_S00_AXI_inst (
    51         .S_AXI_ACLK(s00_axi_aclk),
    52         .S_AXI_ARESETN(s00_axi_aresetn),
    53         .S_AXI_AWADDR(s00_axi_awaddr),
    54         .S_AXI_AWPROT(s00_axi_awprot),
    55         .S_AXI_AWVALID(s00_axi_awvalid),
    56         .S_AXI_AWREADY(s00_axi_awready),
    57         .S_AXI_WDATA(s00_axi_wdata),
    58         .S_AXI_WSTRB(s00_axi_wstrb),
    59         .S_AXI_WVALID(s00_axi_wvalid),
    60         .S_AXI_WREADY(s00_axi_wready),
    61         .S_AXI_BRESP(s00_axi_bresp),
    62         .S_AXI_BVALID(s00_axi_bvalid),
    63         .S_AXI_BREADY(s00_axi_bready),
    64         .S_AXI_ARADDR(s00_axi_araddr),
    65         .S_AXI_ARPROT(s00_axi_arprot),
    66         .S_AXI_ARVALID(s00_axi_arvalid),
    67         .S_AXI_ARREADY(s00_axi_arready),
    68         .S_AXI_RDATA(s00_axi_rdata),
    69         .S_AXI_RRESP(s00_axi_rresp),
    70         .S_AXI_RVALID(s00_axi_rvalid),
    71         .S_AXI_RREADY(s00_axi_rready),
    72 
    73         .led(led)
    74     );
    75 
    76     // Add user logic here
    77 
    78     // User logic ends
    79 
    80     endmodule
    pwm_led_ip_v1_0.v
      1 `timescale 1 ns / 1 ps
      2 
      3     module pwm_led_ip_v1_0_S00_AXI #
      4     (
      5         // Users to add parameters here
      6         parameter FREQ_STEP = 10'd100,
      7         // User parameters ends 
      8         // Do not modify the parameters beyond this line
      9 
     10         // Width of S_AXI data bus
     11         parameter integer C_S_AXI_DATA_WIDTH    = 32,
     12         // Width of S_AXI address bus
     13         parameter integer C_S_AXI_ADDR_WIDTH    = 4
     14     )
     15     (
     16         // Users to add ports here
     17         output led,
     18         // User ports ends
     19         // Do not modify the ports beyond this line
     20 
     21         // Global Clock Signal
     22         input wire  S_AXI_ACLK,
     23         // Global Reset Signal. This Signal is Active LOW
     24         input wire  S_AXI_ARESETN,
     25         // Write address (issued by master, acceped by Slave)
     26         input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_AWADDR,
     27         // Write channel Protection type. This signal indicates the
     28             // privilege and security level of the transaction, and whether
     29             // the transaction is a data access or an instruction access.
     30         input wire [2 : 0] S_AXI_AWPROT,
     31         // Write address valid. This signal indicates that the master signaling
     32             // valid write address and control information.
     33         input wire  S_AXI_AWVALID,
     34         // Write address ready. This signal indicates that the slave is ready
     35             // to accept an address and associated control signals.
     36         output wire  S_AXI_AWREADY,
     37         // Write data (issued by master, acceped by Slave) 
     38         input wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_WDATA,
     39         // Write strobes. This signal indicates which byte lanes hold
     40             // valid data. There is one write strobe bit for each eight
     41             // bits of the write data bus.    
     42         input wire [(C_S_AXI_DATA_WIDTH/8)-1 : 0] S_AXI_WSTRB,
     43         // Write valid. This signal indicates that valid write
     44             // data and strobes are available.
     45         input wire  S_AXI_WVALID,
     46         // Write ready. This signal indicates that the slave
     47             // can accept the write data.
     48         output wire  S_AXI_WREADY,
     49         // Write response. This signal indicates the status
     50             // of the write transaction.
     51         output wire [1 : 0] S_AXI_BRESP,
     52         // Write response valid. This signal indicates that the channel
     53             // is signaling a valid write response.
     54         output wire  S_AXI_BVALID,
     55         // Response ready. This signal indicates that the master
     56             // can accept a write response.
     57         input wire  S_AXI_BREADY,
     58         // Read address (issued by master, acceped by Slave)
     59         input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_ARADDR,
     60         // Protection type. This signal indicates the privilege
     61             // and security level of the transaction, and whether the
     62             // transaction is a data access or an instruction access.
     63         input wire [2 : 0] S_AXI_ARPROT,
     64         // Read address valid. This signal indicates that the channel
     65             // is signaling valid read address and control information.
     66         input wire  S_AXI_ARVALID,
     67         // Read address ready. This signal indicates that the slave is
     68             // ready to accept an address and associated control signals.
     69         output wire  S_AXI_ARREADY,
     70         // Read data (issued by slave)
     71         output wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_RDATA,
     72         // Read response. This signal indicates the status of the
     73             // read transfer.
     74         output wire [1 : 0] S_AXI_RRESP,
     75         // Read valid. This signal indicates that the channel is
     76             // signaling the required read data.
     77         output wire  S_AXI_RVALID,
     78         // Read ready. This signal indicates that the master can
     79             // accept the read data and response information.
     80         input wire  S_AXI_RREADY
     81     );
     82 
     83     // AXI4LITE signals
     84     reg [C_S_AXI_ADDR_WIDTH-1 : 0]     axi_awaddr;
     85     reg      axi_awready;
     86     reg      axi_wready;
     87     reg [1 : 0]     axi_bresp;
     88     reg      axi_bvalid;
     89     reg [C_S_AXI_ADDR_WIDTH-1 : 0]     axi_araddr;
     90     reg      axi_arready;
     91     reg [C_S_AXI_DATA_WIDTH-1 : 0]     axi_rdata;
     92     reg [1 : 0]     axi_rresp;
     93     reg      axi_rvalid;
     94 
     95     // Example-specific design signals
     96     // local parameter for addressing 32 bit / 64 bit C_S_AXI_DATA_WIDTH
     97     // ADDR_LSB is used for addressing 32/64 bit registers/memories
     98     // ADDR_LSB = 2 for 32 bits (n downto 2)
     99     // ADDR_LSB = 3 for 64 bits (n downto 3)
    100     localparam integer ADDR_LSB = (C_S_AXI_DATA_WIDTH/32) + 1;
    101     localparam integer OPT_MEM_ADDR_BITS = 1;
    102     //----------------------------------------------
    103     //-- Signals for user logic register space example
    104     //------------------------------------------------
    105     //-- Number of Slave Registers 4
    106     reg [C_S_AXI_DATA_WIDTH-1:0]    slv_reg0;
    107     reg [C_S_AXI_DATA_WIDTH-1:0]    slv_reg1;
    108     reg [C_S_AXI_DATA_WIDTH-1:0]    slv_reg2;
    109     reg [C_S_AXI_DATA_WIDTH-1:0]    slv_reg3;
    110     wire     slv_reg_rden;
    111     wire     slv_reg_wren;
    112     reg [C_S_AXI_DATA_WIDTH-1:0]     reg_data_out;
    113     integer     byte_index;
    114     reg     aw_en;
    115 
    116     // I/O Connections assignments
    117 
    118     assign S_AXI_AWREADY    = axi_awready;
    119     assign S_AXI_WREADY    = axi_wready;
    120     assign S_AXI_BRESP    = axi_bresp;
    121     assign S_AXI_BVALID    = axi_bvalid;
    122     assign S_AXI_ARREADY    = axi_arready;
    123     assign S_AXI_RDATA    = axi_rdata;
    124     assign S_AXI_RRESP    = axi_rresp;
    125     assign S_AXI_RVALID    = axi_rvalid;
    126     // Implement axi_awready generation
    127     // axi_awready is asserted for one S_AXI_ACLK clock cycle when both
    128     // S_AXI_AWVALID and S_AXI_WVALID are asserted. axi_awready is
    129     // de-asserted when reset is low.
    130 
    131     always @( posedge S_AXI_ACLK )
    132     begin
    133       if ( S_AXI_ARESETN == 1'b0 )
    134         begin
    135           axi_awready <= 1'b0;
    136           aw_en <= 1'b1;
    137         end 
    138       else
    139         begin    
    140           if (~axi_awready && S_AXI_AWVALID && S_AXI_WVALID && aw_en)
    141             begin
    142               // slave is ready to accept write address when 
    143               // there is a valid write address and write data
    144               // on the write address and data bus. This design 
    145               // expects no outstanding transactions. 
    146               axi_awready <= 1'b1;
    147               aw_en <= 1'b0;
    148             end
    149             else if (S_AXI_BREADY && axi_bvalid)
    150                 begin
    151                   aw_en <= 1'b1;
    152                   axi_awready <= 1'b0;
    153                 end
    154           else           
    155             begin
    156               axi_awready <= 1'b0;
    157             end
    158         end 
    159     end       
    160 
    161     // Implement axi_awaddr latching
    162     // This process is used to latch the address when both 
    163     // S_AXI_AWVALID and S_AXI_WVALID are valid. 
    164 
    165     always @( posedge S_AXI_ACLK )
    166     begin
    167       if ( S_AXI_ARESETN == 1'b0 )
    168         begin
    169           axi_awaddr <= 0;
    170         end 
    171       else
    172         begin    
    173           if (~axi_awready && S_AXI_AWVALID && S_AXI_WVALID && aw_en)
    174             begin
    175               // Write Address latching 
    176               axi_awaddr <= S_AXI_AWADDR;
    177             end
    178         end 
    179     end       
    180 
    181     // Implement axi_wready generation
    182     // axi_wready is asserted for one S_AXI_ACLK clock cycle when both
    183     // S_AXI_AWVALID and S_AXI_WVALID are asserted. axi_wready is 
    184     // de-asserted when reset is low. 
    185 
    186     always @( posedge S_AXI_ACLK )
    187     begin
    188       if ( S_AXI_ARESETN == 1'b0 )
    189         begin
    190           axi_wready <= 1'b0;
    191         end 
    192       else
    193         begin    
    194           if (~axi_wready && S_AXI_WVALID && S_AXI_AWVALID && aw_en )
    195             begin
    196               // slave is ready to accept write data when 
    197               // there is a valid write address and write data
    198               // on the write address and data bus. This design 
    199               // expects no outstanding transactions. 
    200               axi_wready <= 1'b1;
    201             end
    202           else
    203             begin
    204               axi_wready <= 1'b0;
    205             end
    206         end 
    207     end       
    208 
    209     // Implement memory mapped register select and write logic generation
    210     // The write data is accepted and written to memory mapped registers when
    211     // axi_awready, S_AXI_WVALID, axi_wready and S_AXI_WVALID are asserted. Write strobes are used to
    212     // select byte enables of slave registers while writing.
    213     // These registers are cleared when reset (active low) is applied.
    214     // Slave register write enable is asserted when valid address and data are available
    215     // and the slave is ready to accept the write address and write data.
    216     assign slv_reg_wren = axi_wready && S_AXI_WVALID && axi_awready && S_AXI_AWVALID;
    217 
    218     always @( posedge S_AXI_ACLK )
    219     begin
    220       if ( S_AXI_ARESETN == 1'b0 )
    221         begin
    222           slv_reg0 <= 0;
    223           slv_reg1 <= 0;
    224           slv_reg2 <= 0;
    225           slv_reg3 <= 0;
    226         end 
    227       else begin
    228         if (slv_reg_wren)
    229           begin
    230             case ( axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
    231               2'h0:
    232                 for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
    233                   if ( S_AXI_WSTRB[byte_index] == 1 ) begin
    234                     // Respective byte enables are asserted as per write strobes 
    235                     // Slave register 0
    236                     slv_reg0[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
    237                   end  
    238               2'h1:
    239                 for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
    240                   if ( S_AXI_WSTRB[byte_index] == 1 ) begin
    241                     // Respective byte enables are asserted as per write strobes 
    242                     // Slave register 1
    243                     slv_reg1[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
    244                   end  
    245               2'h2:
    246                 for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
    247                   if ( S_AXI_WSTRB[byte_index] == 1 ) begin
    248                     // Respective byte enables are asserted as per write strobes 
    249                     // Slave register 2
    250                     slv_reg2[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
    251                   end  
    252               2'h3:
    253                 for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
    254                   if ( S_AXI_WSTRB[byte_index] == 1 ) begin
    255                     // Respective byte enables are asserted as per write strobes 
    256                     // Slave register 3
    257                     slv_reg3[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
    258                   end  
    259               default : begin
    260                           slv_reg0 <= slv_reg0;
    261                           slv_reg1 <= slv_reg1;
    262                           slv_reg2 <= slv_reg2;
    263                           slv_reg3 <= slv_reg3;
    264                         end
    265             endcase
    266           end
    267       end
    268     end    
    269 
    270     // Implement write response logic generation
    271     // The write response and response valid signals are asserted by the slave 
    272     // when axi_wready, S_AXI_WVALID, axi_wready and S_AXI_WVALID are asserted.  
    273     // This marks the acceptance of address and indicates the status of 
    274     // write transaction.
    275 
    276     always @( posedge S_AXI_ACLK )
    277     begin
    278       if ( S_AXI_ARESETN == 1'b0 )
    279         begin
    280           axi_bvalid  <= 0;
    281           axi_bresp   <= 2'b0;
    282         end 
    283       else
    284         begin    
    285           if (axi_awready && S_AXI_AWVALID && ~axi_bvalid && axi_wready && S_AXI_WVALID)
    286             begin
    287               // indicates a valid write response is available
    288               axi_bvalid <= 1'b1;
    289               axi_bresp  <= 2'b0; // 'OKAY' response 
    290             end                   // work error responses in future
    291           else
    292             begin
    293               if (S_AXI_BREADY && axi_bvalid) 
    294                 //check if bready is asserted while bvalid is high) 
    295                 //(there is a possibility that bready is always asserted high)   
    296                 begin
    297                   axi_bvalid <= 1'b0; 
    298                 end  
    299             end
    300         end
    301     end   
    302 
    303     // Implement axi_arready generation
    304     // axi_arready is asserted for one S_AXI_ACLK clock cycle when
    305     // S_AXI_ARVALID is asserted. axi_awready is 
    306     // de-asserted when reset (active low) is asserted. 
    307     // The read address is also latched when S_AXI_ARVALID is 
    308     // asserted. axi_araddr is reset to zero on reset assertion.
    309 
    310     always @( posedge S_AXI_ACLK )
    311     begin
    312       if ( S_AXI_ARESETN == 1'b0 )
    313         begin
    314           axi_arready <= 1'b0;
    315           axi_araddr  <= 32'b0;
    316         end 
    317       else
    318         begin    
    319           if (~axi_arready && S_AXI_ARVALID)
    320             begin
    321               // indicates that the slave has acceped the valid read address
    322               axi_arready <= 1'b1;
    323               // Read address latching
    324               axi_araddr  <= S_AXI_ARADDR;
    325             end
    326           else
    327             begin
    328               axi_arready <= 1'b0;
    329             end
    330         end 
    331     end       
    332 
    333     // Implement axi_arvalid generation
    334     // axi_rvalid is asserted for one S_AXI_ACLK clock cycle when both 
    335     // S_AXI_ARVALID and axi_arready are asserted. The slave registers 
    336     // data are available on the axi_rdata bus at this instance. The 
    337     // assertion of axi_rvalid marks the validity of read data on the 
    338     // bus and axi_rresp indicates the status of read transaction.axi_rvalid 
    339     // is deasserted on reset (active low). axi_rresp and axi_rdata are 
    340     // cleared to zero on reset (active low).  
    341     always @( posedge S_AXI_ACLK )
    342     begin
    343       if ( S_AXI_ARESETN == 1'b0 )
    344         begin
    345           axi_rvalid <= 0;
    346           axi_rresp  <= 0;
    347         end 
    348       else
    349         begin    
    350           if (axi_arready && S_AXI_ARVALID && ~axi_rvalid)
    351             begin
    352               // Valid read data is available at the read data bus
    353               axi_rvalid <= 1'b1;
    354               axi_rresp  <= 2'b0; // 'OKAY' response
    355             end   
    356           else if (axi_rvalid && S_AXI_RREADY)
    357             begin
    358               // Read data is accepted by the master
    359               axi_rvalid <= 1'b0;
    360             end                
    361         end
    362     end    
    363 
    364     // Implement memory mapped register select and read logic generation
    365     // Slave register read enable is asserted when valid address is available
    366     // and the slave is ready to accept the read address.
    367     assign slv_reg_rden = axi_arready & S_AXI_ARVALID & ~axi_rvalid;
    368     always @(*)
    369     begin
    370           // Address decoding for reading registers
    371           case ( axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
    372             2'h0   : reg_data_out <= slv_reg0;
    373             2'h1   : reg_data_out <= slv_reg1;
    374             2'h2   : reg_data_out <= slv_reg2;
    375             2'h3   : reg_data_out <= slv_reg3;
    376             default : reg_data_out <= 0;
    377           endcase
    378     end
    379 
    380     // Output register or memory read data
    381     always @( posedge S_AXI_ACLK )
    382     begin
    383       if ( S_AXI_ARESETN == 1'b0 )
    384         begin
    385           axi_rdata  <= 0;
    386         end 
    387       else
    388         begin    
    389           // When there is a valid read address (S_AXI_ARVALID) with 
    390           // acceptance of read address by the slave (axi_arready), 
    391           // output the read dada 
    392           if (slv_reg_rden)
    393             begin
    394               axi_rdata <= reg_data_out;     // register read data
    395             end   
    396         end
    397     end    
    398 
    399     // Add user logic here
    400     pwm#(.FREQ_STEP(FREQ_STEP))
    401     u_pwm(
    402     .clk            (S_AXI_ACLK),
    403     .rst_n          (S_AXI_ARESETN),
    404     .sw_en          (slv_reg0[0]),
    405     .sw_set_en      (slv_reg1[0]),
    406     .sw_freq_step   (slv_reg2[10-1:0]),
    407     .led            (led)
    408     );
    409     // User logic ends
    410 
    411     endmodule
    pwm_led_ip_v1_0_S00_AXI.v

      最后重新封装

      接下来搭建硬件IP子系统。

       和之前相比只是添加了pwm_led_ip_0,并连接在AXI Interconnect的另一个Master接口上。使用SystemILA抓取总线信号以备后续观察。还是同样的操作流程:生成输出文件,生成HDL Wrapper,添加管脚约束文件,综合,实现,生成比特流并导出硬件,启动SDK软件环境。

    四、软件编程与调试

       其实CPU控制自定义IP的方式就是读写数据,写就是对指针赋值,读就是返回指针所指向地址中的数据,分别使用Xil_Out32()和Xil_In32()实现。创建pwm_led_ip.h文件,进行地址宏定义并编写配置函数。为了更好地实现软件库的封装和扩展,创建environment.h文件来include不同的库以及宏定义、全局变量定义。

      软件代码如下:

      1 /*
      2  * main.c
      3  *
      4  *  Created on: 2020年2月22日
      5  *      Author: s
      6  */
      7 
      8 
      9 #include "environment.h"
     10 
     11 void GpioHandler(void *CallbackRef);
     12 int setupIntSystem(XScuGic *IntcInstancePtr,XGpio *gpioInstancePtr
     13         ,u32 IntrId);
     14 
     15 int main()
     16 {
     17     int Status;
     18     u8 i=0;
     19     u32 sys_led_out=0x1;
     20     u32 data_r;
     21     freq_step_value = 10;
     22 
     23     Status = gpiops_initialize(&GpioPs,GPIOPS_DEVICE_ID);
     24     if (Status != XST_SUCCESS) {
     25         return XST_FAILURE;
     26     }
     27 
     28     Status = gpio_initialize(&Gpio,GPIO_DEVICE_ID);
     29     if (Status != XST_SUCCESS) {
     30         return XST_FAILURE;
     31     }
     32 
     33     /*
     34      * Set the direction for the pin to be output and
     35     * Enable the Output enable for the LED Pin.
     36      */
     37     gpiops_setOutput(&GpioPs,MIO_OUT_PIN_INDEX);
     38 
     39     for(i=0;i<LOOP_NUM;i++){
     40         gpiops_setOutput(&GpioPs,EMIO_OUT_PIN_BASE_INDEX+i);
     41     }
     42 
     43     gpio_setDirect(&Gpio, 1,GPIO_CHANNEL1);
     44 
     45     Status = setupIntSystem(&Intc,&Gpio,INTC_GPIO_INTERRUPT_ID);
     46     if (Status != XST_SUCCESS) {
     47             return XST_FAILURE;
     48         }
     49 
     50     Status = pwm_led_setFreqStep(freq_step_value);
     51     if (Status != XST_SUCCESS) {
     52             return XST_FAILURE;
     53         }
     54 
     55     printf("Initialization finish.
    ");
     56 
     57     while(1){
     58 
     59         for(i=0;i<LOOP_NUM;i++){
     60             if(int_flag == 0)
     61             {
     62                 gpiops_outputValue(&GpioPs, EMIO_OUT_PIN_BASE_INDEX+i, 0x1);
     63                 usleep(200*1000);
     64                 gpiops_outputValue(&GpioPs, EMIO_OUT_PIN_BASE_INDEX+i, 0x0);
     65             }
     66             else
     67             {
     68                 gpiops_outputValue(&GpioPs, EMIO_OUT_PIN_BASE_INDEX+LOOP_NUM-1-i, 0x1);
     69                 usleep(200*1000);
     70                 gpiops_outputValue(&GpioPs, EMIO_OUT_PIN_BASE_INDEX+LOOP_NUM-1-i, 0x0);
     71             }
     72         }
     73 
     74         gpiops_outputValue(&GpioPs, MIO_OUT_PIN_INDEX, sys_led_out);
     75         sys_led_out  = sys_led_out == 0x0 ? 0x1 : 0x0;
     76     }
     77     return 0;
     78 }
     79 
     80 
     81 
     82 int setupIntSystem(XScuGic *IntcInstancePtr,XGpio *gpioInstancePtr
     83         ,u32 IntrId)
     84 {
     85     int Result;
     86     /*
     87     * Initialize the interrupt controller driver so that it is ready to
     88     * use.
     89     */
     90 
     91     Result = gic_initialize(&Intc,INTC_DEVICE_ID);
     92     if (Result != XST_SUCCESS) {
     93             return XST_FAILURE;
     94         }
     95 
     96     XScuGic_SetPriorityTriggerType(IntcInstancePtr, IntrId,
     97                         0xA0, 0x3);
     98 
     99     /*
    100     * Connect the interrupt handler that will be called when an
    101      * interrupt occurs for the device.
    102      */
    103     Result = XScuGic_Connect(IntcInstancePtr, IntrId,
    104                 (Xil_ExceptionHandler)GpioHandler, gpioInstancePtr);
    105     if (Result != XST_SUCCESS) {
    106         return Result;
    107     }
    108 
    109     /* Enable the interrupt for the GPIO device.*/
    110     XScuGic_Enable(IntcInstancePtr, IntrId);
    111 
    112     /*
    113      * Enable the GPIO channel interrupts so that push button can be
    114     * detected and enable interrupts for the GPIO device
    115     */
    116     XGpio_InterruptEnable(gpioInstancePtr,GPIO_CHANNEL1);
    117     XGpio_InterruptGlobalEnable(gpioInstancePtr);
    118 
    119     /*
    120     * Initialize the exception table and register the interrupt
    121     * controller handler with the exception table
    122     */
    123     exception_enable(&Intc);
    124 
    125     IntrFlag = 0;
    126 
    127     return XST_SUCCESS;
    128 }
    129 
    130 void GpioHandler(void *CallbackRef)
    131 {
    132     XGpio *GpioPtr = (XGpio *)CallbackRef;
    133     u32 gpio_inputValue;
    134 
    135 
    136     /* Clear the Interrupt */
    137     XGpio_InterruptClear(GpioPtr, GPIO_CHANNEL1);
    138     printf("Input interrupt routine.
    ");
    139 
    140     //IntrFlag = 1;
    141     gpio_inputValue = gpio_readValue(GpioPtr, 1);
    142     switch(gpio_inputValue)
    143     {
    144     case 30:
    145         //printf("button up
    ");
    146         freq_step_value+=10;
    147         pwm_led_setFreqStep(freq_step_value);
    148         break;
    149     case 29:
    150         printf("button center
    ");
    151         break;
    152     case 27:
    153         //printf("button left
    ");
    154         int_flag = 0;
    155         break;
    156     case 23:
    157         //printf("button right
    ");
    158         int_flag = 1;
    159         break;
    160     case 15:
    161         //print("button down
    ");
    162         freq_step_value-=10;
    163         pwm_led_setFreqStep(freq_step_value);
    164         break;
    165     }
    166 
    167 }
    main.c
     1 /*
     2  * environment.h
     3  *
     4  *  Created on: 2020年3月2日
     5  *      Author: s
     6  */
     7 
     8 #ifndef SRC_ENVIRONMENT_H_
     9 #define SRC_ENVIRONMENT_H_
    10 
    11 #include "xparameters.h"
    12 #include <xil_printf.h>
    13 #include "sleep.h"
    14 #include "xstatus.h"
    15 
    16 #include "gpiops.h"
    17 #include "gpio.h"
    18 #include "pwm_led_ip.h"
    19 #include "gic.h"
    20 
    21 XGpioPs GpioPs;    /* The driver instance for GPIO Device. */
    22 XGpio Gpio;
    23 XScuGic Intc; /* The Instance of the Interrupt Controller Driver */
    24 
    25 
    26 
    27 #define printf            xil_printf    /* Smalller foot-print printf */
    28 #define LOOP_NUM 4
    29 
    30 
    31 u32 MIO_OUT_PIN_INDEX =7; /* LED button */
    32 u32 EMIO_OUT_PIN_BASE_INDEX = 54;
    33 volatile u32 IntrFlag; /* Interrupt Handler Flag */
    34 
    35 #endif /* SRC_ENVIRONMENT_H_ */
    environment.h
     1 /*
     2  * pwm_led_ip.h
     3  *
     4  *  Created on: 2020年3月2日
     5  *      Author: s
     6  */
     7 
     8 #ifndef SRC_PWM_LED_IP_H_
     9 #define SRC_PWM_LED_IP_H_
    10 
    11 #define PWM_LED_IP_S00_AXI_SLV_REG0_OFFSET 0
    12 #define PWM_LED_IP_S00_AXI_SLV_REG1_OFFSET 4
    13 #define PWM_LED_IP_S00_AXI_SLV_REG2_OFFSET 8
    14 #define PWM_LED_IP_S00_AXI_SLV_REG3_OFFSET 12
    15 
    16 #define PWM_LED_IP_BASEADDR XPAR_PWM_LED_IP_0_S00_AXI_BASEADDR
    17 #define FREQ_STEP_SET_VALUE 30
    18 
    19 #define PWM_LED_IP_REG_EN (PWM_LED_IP_BASEADDR+PWM_LED_IP_S00_AXI_SLV_REG0_OFFSET)
    20 #define PWM_LED_IP_REG_SET_EN (PWM_LED_IP_BASEADDR+PWM_LED_IP_S00_AXI_SLV_REG1_OFFSET)
    21 #define PWM_LED_IP_REG_FREQ_STEP (PWM_LED_IP_BASEADDR+PWM_LED_IP_S00_AXI_SLV_REG2_OFFSET)
    22 #define PWM_LED_IP_REG_RESERVED (PWM_LED_IP_BASEADDR+PWM_LED_IP_S00_AXI_SLV_REG3_OFFSET)
    23 
    24 volatile u32 freq_step_value;
    25 
    26 int pwm_led_setFreqStep(u32 value)
    27 {
    28 
    29     u32 data_r;
    30     Xil_Out32(PWM_LED_IP_REG_EN,0x01);
    31     Xil_Out32(PWM_LED_IP_REG_SET_EN,0x01);
    32     Xil_Out32(PWM_LED_IP_REG_FREQ_STEP,value);
    33     data_r = Xil_In32(PWM_LED_IP_REG_FREQ_STEP);
    34     Xil_Out32(PWM_LED_IP_REG_SET_EN,0x00);
    35     if(data_r == value)
    36         return XST_SUCCESS;
    37     else
    38         return XST_FAILURE;
    39 
    40 }
    41 
    42 #endif /* SRC_PWM_LED_IP_H_ */
    pwm_led_ip.h

      其他文件与上一篇ZYNQ入门实例博文相同。Run程序后多次按下按键,从串口terminal可以看出系统初始化成功,进入按键中断回调函数。开发板上呼吸灯频率也随着按键按下在变化。

      最后打开VIVADO硬件管理器,观察AXI总线波形:

       按下步长值增加按键后,会有四次写数据操作,正好对应pwm_led_setFreqStep function中的四次Xil_Out32调用。每次写后一个时钟周期写响应通道BVALID拉高一个时钟周期证明写正确。

       再来观察用于确认写入无误的读操作对应总线波形:

       读取数据为40,与写入一致。到此功能定义、设计规划、硬件逻辑设计仿真、IP封装、子系统搭建、软件设计、板级调试的流程全部走完。

  • 相关阅读:
    JDBC_增删改
    JSP内置对象作用域
    497. Random Point in Non-overlapping Rectangles
    921. Minimum Add to Make Parentheses Valid
    946. Validate Stack Sequences
    824. Goat Latin
    Acwing 165 小猫爬山 (dfs)
    Codeforces #656 Problem D
    Codeforces #656 Problem C
    对象属性拷贝工具类大全
  • 原文地址:https://www.cnblogs.com/moluoqishi/p/12390970.html
Copyright © 2011-2022 走看看