ZYNQ Custom AXI Bus IP Application-PWM for Respiratory Light Effect

1. Preface

In the case of high real-time requirements, the way the CPU software is executed obviously can not meet the requirements, which requires hardware logic to implement some functions.For a custom IP core to be accessed by the CPU, it must have a bus interface.ZYNQ uses AXI BUS to achieve data interaction between PS and PL.This paper takes PWM as an example to design a custom AXI bus IP to demonstrate how to use the architecture of ARM+GA flexibly.

Function Definition: Add a custom IP core to the system explained in the previous entry ZYNQ example blog, whose output drives LED and so on to achieve respiratory lamp effect.And the software enables it by configuring registers, turns it on/off, and chooses the step of duty cycle change.In addition, the increase or decrease of the change step of duty cycle can be completed by key operation.

Platform: MIZ702N (ZYNQ-7020)

Software: VIVADO+SDK 2017

Note: Custom IP logic design uses Mingdeyang to Simplicity design method

2. PWM IP Design

PWM achieves its control purpose by controlling the duty cycle of the periodic pulse signal, that is, by changing the duration of the high level in a fixed period.Pulse cycles require a counter to be timed, and duty cycles from low to high and from high to low also require a counter to indicate, so two nested counters, cnt_cyc and cnt_mode, are used here.In addition to waiting for the cnt_cyc count to complete, the addition of a cnt_model also takes into account the change in the duty cycle.

You can use the position along the descending edge to indicate the duty cycle, and the closer the position is to the periodic value, the higher the duty cycle.In mode 0, the downward position increases in step until it is greater than or equal to the periodic value, while in mode 1, the downward position decreases in step until it is less than the step.Mode 0 and mode 1 are indicated using two signals up_stage and down_stage, respectively.As for the step value, it is updated when the configuration is valid, otherwise the default value is used.The module's final output signal is less than 1 along the cycle counter, and vice versa.Design complete, code:

  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,//Frequency 100 MHz 10ns
 25 input                       rst_n,
 26 input                       sw_en,//Output Enables
 27 input                       sw_set_en,//Step settings enable
 28 input       [10-1:0]        sw_freq_step,//Step value
 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 //Cycle counter count 50 ms=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 //Mode counter 0-Duty cycle increment 1-Duty cycle decreases
 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 //Change Step Settings
 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 //Pulse drop counter value along corresponding period
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//Duty Cycle Increasing Stage
108             if(neg_loc < CNT_CYC_MAX)
109                 neg_loc <= neg_loc + freq_step;
110         end
111         else if(down_stage )begin//Duty Cycle Decreasing Stage
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 //output
126 always@(posedge clk)begin
127     if(~rst_n)begin
128         led <= 1'b0;//High level lighting
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

Because VIVADO integration, slow layout and wiring, and time-consuming hardware and software cascade debugging, simulation is very important.Write a simple testbench that defines the update_freq_step task update step.There is some benefit to using System Verilog syntax here.First, the single-drive signal can be uniformly defined as a logic variable type, and secondly, the wait time can specify the unit.

 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) ,//Frequency 100 MHz 10ns
38 .rst_n         (rst_n) ,
39 .sw_en         (sw_en) ,//Output Enables
40 .sw_set_en     (sw_set_en) ,//Step settings enable
41 .sw_freq_step  (sw_freq_step) ,//Step value
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

The design is simple, simply use the VIVADO emulator to observe the waveform:

You can see that the duty cycle of the output signal led changes more slowly than it does continuously, when the freq_step is updated to 50.

The difference between the two neg_loc values before configuration and after update is 100 and 50, respectively.The above proves that the logic function is correct.

3. Hardware System Building

* The PWM module has not been completely designed yet, so another layer of bus Wrapper is needed to be accessed by the CPU.Create AXI Bus IP

Edit in the encapsulator.

The final IP structure is as follows:

The specific operation is not described much, but presented directly in code:

 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

Last Re-encapsulation

Next, set up the hardware IP subsystem.

Comparing with the previous one, only pwm_led_ip_0 has been added and connected to another Master interface of AXI Interconnect.Use SystemILA to capture bus signals for subsequent observation.It's the same process: generating output files, generating HDL Wrapper, adding pin constraint files, synthesizing, implementing, generating bitstream and exporting hardware, starting SDK software environment.

4. Software Programming and Debugging

In fact, the way the CPU controls custom IP is to read and write data, write is to assign values to the pointer, and read is to return the data in the address that the pointer points to, using Xil_Out32() and Xil_In32() respectively.Create a pwm_led_ip.h file, define address macros, and write configuration functions.To better encapsulate and extend software libraries, create an environment.h file to include different libraries as well as macro and global variable definitions.

The software code is as follows:

  1 /*
  2  * main.c
  3  *
  4  *  Created on: 2020 February 22, 2000
  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.\n");
 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.\n");
139 
140     //IntrFlag = 1;
141     gpio_inputValue = gpio_readValue(GpioPtr, 1);
142     switch(gpio_inputValue)
143     {
144     case 30:
145         //printf("button up\n");
146         freq_step_value+=10;
147         pwm_led_setFreqStep(freq_step_value);
148         break;
149     case 29:
150         printf("button center\n");
151         break;
152     case 27:
153         //printf("button left\n");
154         int_flag = 0;
155         break;
156     case 23:
157         //printf("button right\n");
158         int_flag = 1;
159         break;
160     case 15:
161         //print("button down\n");
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 March 2, 2000
 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 March 2, 2000
 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

The other files are the same as the previous entry to ZYNQ example blog.After the Run program, the key is pressed several times. From the serial port terminal, you can see that the system initialized successfully and entered the key interrupt callback function.The frequency of breathing lights on the development board also changes with the press of keys.

Finally, open the VIVADO hardware manager to observe the AXI bus waveform:

After pressing the step value to increase the key, there will be four writes to the data, which corresponds to four Xil_Out32 calls in the pwm_led_setFreqStep function.Write response channel BVALID one clock cycle after each write to verify correct writing.

Also observe the corresponding bus waveforms for the read operation used to confirm write correctness:

Read data is 40, consistent with write.The process of function definition, design planning, hardware logic design simulation, IP encapsulation, subsystem building, software design, board debugging has been completed.

Tags: Verilog SDK less emulator

Posted on Mon, 02 Mar 2020 11:07:16 -0500 by alanrenouf