├── Driver
├── main.c
└── PWM_Generator.h
├── HDL
├── pwm_gen.v
├── PWM_Generator_v1_0.v
└── PWM_Generator_v1_0_S_AXI.v
└── README.md
/Driver/main.c:
--------------------------------------------------------------------------------
1 | /*
2 | * main.c
3 | *
4 | * Created on: May 18, 2014
5 | * Author: Devon
6 | */
7 |
8 | #include "xparameters.h"
9 | #include "xil_types.h"
10 | #include "PWM_Generator.h"
11 |
12 | int main(void)
13 | {
14 | // Set period to 20ms and compare to 5% duty cycle
15 | PWM_GENERATOR_mDisableOutput(XPAR_PWM_GENERATOR_0_S_AXI_BASEADDR);
16 | PWM_GENERATOR_mSetLoad(XPAR_PWM_GENERATOR_0_S_AXI_BASEADDR, 2000000);
17 | PWM_GENERATOR_mSetCompare(XPAR_PWM_GENERATOR_0_S_AXI_BASEADDR, 200000);
18 | PWM_GENERATOR_mEnableOutput(XPAR_PWM_GENERATOR_0_S_AXI_BASEADDR);
19 |
20 | while(1)
21 | {
22 |
23 | }
24 |
25 | return 0;
26 | }
27 |
--------------------------------------------------------------------------------
/HDL/pwm_gen.v:
--------------------------------------------------------------------------------
1 | `timescale 1ns / 1ps
2 | //////////////////////////////////////////////////////////////////////////////////
3 | // Engineer: Devon Andrade
4 | //
5 | // Create Date: 05/18/2014 02:43:10 PM
6 | // Design Name: PWM Generator
7 | // Module Name: pwm_gen
8 | // Project Name: PWM Generator
9 | // Target Devices: xc7z010clg400-1
10 | // Tool Versions: Vivado 2014.1
11 | //
12 | // Parameters:
13 | // WIDTH - How many bits the counter, load, and compare values are
14 | //
15 | // Inputs:
16 | // load - The value at which the counter will reset to zero and set the output
17 | // compare - The value at which the counter will clear the output
18 | // control - Register bit map:
19 | // Bit 0: PWM Enable - Output is enabled if this bit is high, disabled otherwise
20 | //
21 | // Outputs:
22 | // pwm_out - The pulse width modulated signal, or ground if disabled
23 | //////////////////////////////////////////////////////////////////////////////////
24 |
25 |
26 | module pwm_gen
27 | #(parameter WIDTH=32) (
28 | input wire clk, rst,
29 | input wire [WIDTH-1:0] load, compare, control,
30 | output wire pwm_out
31 | );
32 |
33 | reg [WIDTH-1:0] count;
34 | reg pwm_out_r;
35 |
36 | // Make sure output is low if PWM is disabled
37 | assign pwm_out = control[0] & pwm_out_r;
38 |
39 | initial begin
40 | pwm_out_r = 1'b0;
41 | count = {WIDTH{1'b0}};
42 | end
43 |
44 | // The counter
45 | always @(posedge clk) begin
46 | if(!rst)
47 | count = {WIDTH{1'b0}};
48 | else if (count < load && control[0])
49 | count = count + 1;
50 | else
51 | count = 0;
52 | end
53 |
54 | always @(negedge clk) begin
55 | if(!rst)
56 | pwm_out_r = 1'b0;
57 | else begin
58 | case(count)
59 | 0 : pwm_out_r = 1'b1;
60 | compare : pwm_out_r = 1'b0;
61 | default : pwm_out_r = pwm_out_r;
62 | endcase
63 | end
64 | end
65 |
66 | endmodule
67 |
--------------------------------------------------------------------------------
/HDL/PWM_Generator_v1_0.v:
--------------------------------------------------------------------------------
1 |
2 | `timescale 1 ps / 1 ps
3 |
4 | module PWM_Generator_v1_0 #
5 | (
6 | // Users to add parameters here
7 |
8 | // User parameters ends
9 | // Do not modify the parameters beyond this line
10 |
11 |
12 | // Parameters of Axi Slave Bus Interface S_AXI
13 | parameter integer C_S_AXI_DATA_WIDTH = 32,
14 | parameter integer C_S_AXI_ADDR_WIDTH = 4
15 | )
16 | (
17 | // Users to add ports here
18 | output wire pwm_out,
19 | // User ports ends
20 | // Do not modify the ports beyond this line
21 |
22 |
23 | // Ports of Axi Slave Bus Interface S_AXI
24 | input wire s_axi_aclk,
25 | input wire s_axi_aresetn,
26 | input wire [C_S_AXI_ADDR_WIDTH-1 : 0] s_axi_awaddr,
27 | input wire [2 : 0] s_axi_awprot,
28 | input wire s_axi_awvalid,
29 | output wire s_axi_awready,
30 | input wire [C_S_AXI_DATA_WIDTH-1 : 0] s_axi_wdata,
31 | input wire [(C_S_AXI_DATA_WIDTH/8)-1 : 0] s_axi_wstrb,
32 | input wire s_axi_wvalid,
33 | output wire s_axi_wready,
34 | output wire [1 : 0] s_axi_bresp,
35 | output wire s_axi_bvalid,
36 | input wire s_axi_bready,
37 | input wire [C_S_AXI_ADDR_WIDTH-1 : 0] s_axi_araddr,
38 | input wire [2 : 0] s_axi_arprot,
39 | input wire s_axi_arvalid,
40 | output wire s_axi_arready,
41 | output wire [C_S_AXI_DATA_WIDTH-1 : 0] s_axi_rdata,
42 | output wire [1 : 0] s_axi_rresp,
43 | output wire s_axi_rvalid,
44 | input wire s_axi_rready
45 | );
46 | // Instantiation of Axi Bus Interface S_AXI
47 | PWM_Generator_v1_0_S_AXI # (
48 | .C_S_AXI_DATA_WIDTH(C_S_AXI_DATA_WIDTH),
49 | .C_S_AXI_ADDR_WIDTH(C_S_AXI_ADDR_WIDTH)
50 | ) PWM_Generator_v1_0_S_AXI_inst (
51 | .pwm_out(pwm_out),
52 | .S_AXI_ACLK(s_axi_aclk),
53 | .S_AXI_ARESETN(s_axi_aresetn),
54 | .S_AXI_AWADDR(s_axi_awaddr),
55 | .S_AXI_AWPROT(s_axi_awprot),
56 | .S_AXI_AWVALID(s_axi_awvalid),
57 | .S_AXI_AWREADY(s_axi_awready),
58 | .S_AXI_WDATA(s_axi_wdata),
59 | .S_AXI_WSTRB(s_axi_wstrb),
60 | .S_AXI_WVALID(s_axi_wvalid),
61 | .S_AXI_WREADY(s_axi_wready),
62 | .S_AXI_BRESP(s_axi_bresp),
63 | .S_AXI_BVALID(s_axi_bvalid),
64 | .S_AXI_BREADY(s_axi_bready),
65 | .S_AXI_ARADDR(s_axi_araddr),
66 | .S_AXI_ARPROT(s_axi_arprot),
67 | .S_AXI_ARVALID(s_axi_arvalid),
68 | .S_AXI_ARREADY(s_axi_arready),
69 | .S_AXI_RDATA(s_axi_rdata),
70 | .S_AXI_RRESP(s_axi_rresp),
71 | .S_AXI_RVALID(s_axi_rvalid),
72 | .S_AXI_RREADY(s_axi_rready)
73 | );
74 |
75 | // Add user logic here
76 |
77 | // User logic ends
78 |
79 | endmodule
80 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Pulse Width Modulation IP
2 | =========================
3 |
4 | A PWM Module IP core written in Verilog, along with a firmware driver (developed for the Zynq-7000 Programmable SoC).
5 |
6 | This repository doesn't contain the Xilinx Vivado project files needed to utilize it as an IP, but instead cuts out the cruft and shows you the bare logic and how to utilize from within a software project.
7 |
8 |
HDL
9 | In the HDL folder you'll find three files:
10 |
11 | - pwm_gen.v - The actual logic needed to create a PWM generator
12 | - PWM_Generator_v1_0_S_AXI.v - The auto-generated AXI wrapper for the PWM generator (at line 106 you can see the four registers used to control the PWM generator). At the bottom of that file you'll find where I hook up the registers to the PWM generator. This is needed because the Zynq-7000 chip lets the Processing system interact with the Programmable Logic system through an AXI interface. So, the AXI registers created in this file are mapped into the processor system's memory map for manipulation in software.
13 | - PWM_Generator_v1_0.v - The top level module instantiating and wiring up the PWM_Generator_v1_0_S_AXI (which in turn instantiates pwm_gen).
14 |
15 |
16 | There are three registers that control the operation of the PWM module:
17 |
18 | - load - The value at which the counter will reset to zero and set the output
19 | - compare - The value at which the counter will clear the output
20 | - control - Bit 0: PWM Enable - Output is enabled if this bit is high, disabled otherwise
21 |
22 |
23 | The PWM module will continuously count up to the "load" value and once it reaches the load value, will loop back to zero. When the counter is at zero, the output is set high. When the PWM module reaches the "compare" value, it clears the output. So the "high" width of the output signal is exactly "compare" clock ticks long (the clock is whatever clock you connect to the IP in your block design, but it will most likely be generated by the Zynq Processing System). The "low" width of the output signal is exactly "load" - "compare" clock ticks long. If you disable the PWM module (by setting bit zero of the control register to zero) then the output will always be low.
24 |
25 | In other words, the "load" register determines your period, while the "compare" register determines your duty cycle.
26 |
27 | Driver
28 | The "PWM_Generator.h" header file contains helpful macros for manipulating the PWM registers while "main.c" gives an example of using those macros. This code would be used in a Xilinx SDK project created for a Zynq design that utilizes the PWM IP.
--------------------------------------------------------------------------------
/Driver/PWM_Generator.h:
--------------------------------------------------------------------------------
1 |
2 | #ifndef PWM_GENERATOR_H
3 | #define PWM_GENERATOR_H
4 |
5 |
6 | /****************** Include Files ********************/
7 | #include "xil_types.h"
8 | #include "xstatus.h"
9 | #include "xil_io.h"
10 |
11 | #define PWM_GENERATOR_S_AXI_LOAD_REG_OFFSET 0
12 | #define PWM_GENERATOR_S_AXI_COMPARE_REG_OFFSET 4
13 | #define PWM_GENERATOR_S_AXI_CONTROL_REG_OFFSET 8
14 | #define PWM_GENERATOR_S_AXI_UNUSED_REG_OFFSET 12
15 |
16 | /**************************** Type Definitions *****************************/
17 | /**
18 | *
19 | * Write a value to a PWM_GENERATOR register. A 32 bit write is performed.
20 | * If the component is implemented in a smaller width, only the least
21 | * significant data is written.
22 | *
23 | * @param BaseAddress is the base address of the PWM_GENERATORdevice.
24 | * @param RegOffset is the register offset from the base to write to.
25 | * @param Data is the data written to the register.
26 | *
27 | * @return None.
28 | *
29 | * @note
30 | * C-style signature:
31 | * void PWM_GENERATOR_mWriteReg(u32 BaseAddress, unsigned RegOffset, u32 Data)
32 | *
33 | */
34 | #define PWM_GENERATOR_mWriteReg(BaseAddress, RegOffset, Data) \
35 | Xil_Out32((BaseAddress) + (RegOffset), (u32)(Data))
36 |
37 | /**
38 | *
39 | * Read a value from a PWM_GENERATOR register. A 32 bit read is performed.
40 | * If the component is implemented in a smaller width, only the least
41 | * significant data is read from the register. The most significant data
42 | * will be read as 0.
43 | *
44 | * @param BaseAddress is the base address of the PWM_GENERATOR device.
45 | * @param RegOffset is the register offset from the base to write to.
46 | *
47 | * @return Data is the data from the register.
48 | *
49 | * @note
50 | * C-style signature:
51 | * u32 PWM_GENERATOR_mReadReg(u32 BaseAddress, unsigned RegOffset)
52 | *
53 | */
54 | #define PWM_GENERATOR_mReadReg(BaseAddress, RegOffset) \
55 | Xil_In32((BaseAddress) + (RegOffset))
56 |
57 | // Enables PWM Output by setting bit 0 of the control register
58 | #define PWM_GENERATOR_mEnableOutput(BaseAddress) \
59 | PWM_GENERATOR_mWriteReg(BaseAddress, PWM_GENERATOR_S_AXI_CONTROL_REG_OFFSET, 1)
60 |
61 | // Disables PWM Output by clearing bit 0 of the control register
62 | #define PWM_GENERATOR_mDisableOutput(BaseAddress) \
63 | PWM_GENERATOR_mWriteReg(BaseAddress, PWM_GENERATOR_S_AXI_CONTROL_REG_OFFSET, 0)
64 |
65 | // Sets the PWM Generator's load value
66 | #define PWM_GENERATOR_mSetLoad(BaseAddress, Load) \
67 | PWM_GENERATOR_mWriteReg(BaseAddress, PWM_GENERATOR_S_AXI_LOAD_REG_OFFSET, Load)
68 |
69 | // Sets the PWM Generator's compare value
70 | #define PWM_GENERATOR_mSetCompare(BaseAddress, Compare) \
71 | PWM_GENERATOR_mWriteReg(BaseAddress, PWM_GENERATOR_S_AXI_COMPARE_REG_OFFSET, Compare)
72 |
73 | /************************** Function Prototypes ****************************/
74 | /**
75 | *
76 | * Run a self-test on the driver/device. Note this may be a destructive test if
77 | * resets of the device are performed.
78 | *
79 | * If the hardware system is not built correctly, this function may never
80 | * return to the caller.
81 | *
82 | * @param baseaddr_p is the base address of the PWM_GENERATOR instance to be worked on.
83 | *
84 | * @return
85 | *
86 | * - XST_SUCCESS if all self-test code passed
87 | * - XST_FAILURE if any self-test code failed
88 | *
89 | * @note Caching must be turned off for this function to work.
90 | * @note Self test may fail if data memory and device are not on the same bus.
91 | *
92 | */
93 | XStatus PWM_GENERATOR_Reg_SelfTest(void * baseaddr_p);
94 |
95 | #endif // PWM_GENERATOR_H
96 |
--------------------------------------------------------------------------------
/HDL/PWM_Generator_v1_0_S_AXI.v:
--------------------------------------------------------------------------------
1 |
2 | `timescale 1 ps / 1 ps
3 |
4 | module PWM_Generator_v1_0_S_AXI #
5 | (
6 | // Users to add parameters here
7 |
8 | // User parameters ends
9 | // Do not modify the parameters beyond this line
10 |
11 | // Width of S_AXI data bus
12 | parameter integer C_S_AXI_DATA_WIDTH = 32,
13 | // Width of S_AXI address bus
14 | parameter integer C_S_AXI_ADDR_WIDTH = 4
15 | )
16 | (
17 | // Users to add ports here
18 | output wire pwm_out,
19 | // User ports ends
20 | // Do not modify the ports beyond this line
21 |
22 | // Global Clock Signal
23 | input wire S_AXI_ACLK,
24 | // Global Reset Signal. This Signal is Active LOW
25 | input wire S_AXI_ARESETN,
26 | // Write address (issued by master, acceped by Slave)
27 | input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_AWADDR,
28 | // Write channel Protection type. This signal indicates the
29 | // privilege and security level of the transaction, and whether
30 | // the transaction is a data access or an instruction access.
31 | input wire [2 : 0] S_AXI_AWPROT,
32 | // Write address valid. This signal indicates that the master signaling
33 | // valid write address and control information.
34 | input wire S_AXI_AWVALID,
35 | // Write address ready. This signal indicates that the slave is ready
36 | // to accept an address and associated control signals.
37 | output wire S_AXI_AWREADY,
38 | // Write data (issued by master, acceped by Slave)
39 | input wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_WDATA,
40 | // Write strobes. This signal indicates which byte lanes hold
41 | // valid data. There is one write strobe bit for each eight
42 | // bits of the write data bus.
43 | input wire [(C_S_AXI_DATA_WIDTH/8)-1 : 0] S_AXI_WSTRB,
44 | // Write valid. This signal indicates that valid write
45 | // data and strobes are available.
46 | input wire S_AXI_WVALID,
47 | // Write ready. This signal indicates that the slave
48 | // can accept the write data.
49 | output wire S_AXI_WREADY,
50 | // Write response. This signal indicates the status
51 | // of the write transaction.
52 | output wire [1 : 0] S_AXI_BRESP,
53 | // Write response valid. This signal indicates that the channel
54 | // is signaling a valid write response.
55 | output wire S_AXI_BVALID,
56 | // Response ready. This signal indicates that the master
57 | // can accept a write response.
58 | input wire S_AXI_BREADY,
59 | // Read address (issued by master, acceped by Slave)
60 | input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_ARADDR,
61 | // Protection type. This signal indicates the privilege
62 | // and security level of the transaction, and whether the
63 | // transaction is a data access or an instruction access.
64 | input wire [2 : 0] S_AXI_ARPROT,
65 | // Read address valid. This signal indicates that the channel
66 | // is signaling valid read address and control information.
67 | input wire S_AXI_ARVALID,
68 | // Read address ready. This signal indicates that the slave is
69 | // ready to accept an address and associated control signals.
70 | output wire S_AXI_ARREADY,
71 | // Read data (issued by slave)
72 | output wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_RDATA,
73 | // Read response. This signal indicates the status of the
74 | // read transfer.
75 | output wire [1 : 0] S_AXI_RRESP,
76 | // Read valid. This signal indicates that the channel is
77 | // signaling the required read data.
78 | output wire S_AXI_RVALID,
79 | // Read ready. This signal indicates that the master can
80 | // accept the read data and response information.
81 | input wire S_AXI_RREADY
82 | );
83 |
84 | // AXI4LITE signals
85 | reg [C_S_AXI_ADDR_WIDTH-1 : 0] axi_awaddr;
86 | reg axi_awready;
87 | reg axi_wready;
88 | reg [1 : 0] axi_bresp;
89 | reg axi_bvalid;
90 | reg [C_S_AXI_ADDR_WIDTH-1 : 0] axi_araddr;
91 | reg axi_arready;
92 | reg [C_S_AXI_DATA_WIDTH-1 : 0] axi_rdata;
93 | reg [1 : 0] axi_rresp;
94 | reg axi_rvalid;
95 |
96 | // Example-specific design signals
97 | // local parameter for addressing 32 bit / 64 bit C_S_AXI_DATA_WIDTH
98 | // ADDR_LSB is used for addressing 32/64 bit registers/memories
99 | // ADDR_LSB = 2 for 32 bits (n downto 2)
100 | // ADDR_LSB = 3 for 64 bits (n downto 3)
101 | localparam integer ADDR_LSB = (C_S_AXI_DATA_WIDTH/32) + 1;
102 | localparam integer OPT_MEM_ADDR_BITS = 1;
103 | //----------------------------------------------
104 | //-- Signals for user logic register space example
105 | //------------------------------------------------
106 | //-- Number of Slave Registers 4
107 | reg [C_S_AXI_DATA_WIDTH-1:0] load_reg;
108 | reg [C_S_AXI_DATA_WIDTH-1:0] compare_reg;
109 | reg [C_S_AXI_DATA_WIDTH-1:0] control_reg;
110 | reg [C_S_AXI_DATA_WIDTH-1:0] unused_reg;
111 | wire slv_reg_rden;
112 | wire slv_reg_wren;
113 | reg [C_S_AXI_DATA_WIDTH-1:0] reg_data_out;
114 | integer byte_index;
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 | end
137 | else
138 | begin
139 | if (~axi_awready && S_AXI_AWVALID && S_AXI_WVALID)
140 | begin
141 | // slave is ready to accept write address when
142 | // there is a valid write address and write data
143 | // on the write address and data bus. This design
144 | // expects no outstanding transactions.
145 | axi_awready <= 1'b1;
146 | end
147 | else
148 | begin
149 | axi_awready <= 1'b0;
150 | end
151 | end
152 | end
153 |
154 | // Implement axi_awaddr latching
155 | // This process is used to latch the address when both
156 | // S_AXI_AWVALID and S_AXI_WVALID are valid.
157 |
158 | always @( posedge S_AXI_ACLK )
159 | begin
160 | if ( S_AXI_ARESETN == 1'b0 )
161 | begin
162 | axi_awaddr <= 0;
163 | end
164 | else
165 | begin
166 | if (~axi_awready && S_AXI_AWVALID && S_AXI_WVALID)
167 | begin
168 | // Write Address latching
169 | axi_awaddr <= S_AXI_AWADDR;
170 | end
171 | end
172 | end
173 |
174 | // Implement axi_wready generation
175 | // axi_wready is asserted for one S_AXI_ACLK clock cycle when both
176 | // S_AXI_AWVALID and S_AXI_WVALID are asserted. axi_wready is
177 | // de-asserted when reset is low.
178 |
179 | always @( posedge S_AXI_ACLK )
180 | begin
181 | if ( S_AXI_ARESETN == 1'b0 )
182 | begin
183 | axi_wready <= 1'b0;
184 | end
185 | else
186 | begin
187 | if (~axi_wready && S_AXI_WVALID && S_AXI_AWVALID)
188 | begin
189 | // slave is ready to accept write data when
190 | // there is a valid write address and write data
191 | // on the write address and data bus. This design
192 | // expects no outstanding transactions.
193 | axi_wready <= 1'b1;
194 | end
195 | else
196 | begin
197 | axi_wready <= 1'b0;
198 | end
199 | end
200 | end
201 |
202 | // Implement memory mapped register select and write logic generation
203 | // The write data is accepted and written to memory mapped registers when
204 | // axi_awready, S_AXI_WVALID, axi_wready and S_AXI_WVALID are asserted. Write strobes are used to
205 | // select byte enables of slave registers while writing.
206 | // These registers are cleared when reset (active low) is applied.
207 | // Slave register write enable is asserted when valid address and data are available
208 | // and the slave is ready to accept the write address and write data.
209 | assign slv_reg_wren = axi_wready && S_AXI_WVALID && axi_awready && S_AXI_AWVALID;
210 |
211 | always @( posedge S_AXI_ACLK )
212 | begin
213 | if ( S_AXI_ARESETN == 1'b0 )
214 | begin
215 | load_reg <= 0;
216 | compare_reg <= 0;
217 | control_reg <= 0;
218 | unused_reg <= 0;
219 | end
220 | else begin
221 | if (slv_reg_wren)
222 | begin
223 | case ( axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
224 | 2'h0:
225 | for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
226 | if ( S_AXI_WSTRB[byte_index] == 1 ) begin
227 | // Respective byte enables are asserted as per write strobes
228 | // Slave register 0
229 | load_reg[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
230 | end
231 | 2'h1:
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 1
236 | compare_reg[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
237 | end
238 | 2'h2:
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 2
243 | control_reg[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
244 | end
245 | 2'h3:
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 3
250 | unused_reg[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
251 | end
252 | default : begin
253 | load_reg <= load_reg;
254 | compare_reg <= compare_reg;
255 | control_reg <= control_reg;
256 | unused_reg <= unused_reg;
257 | end
258 | endcase
259 | end
260 | end
261 | end
262 |
263 | // Implement write response logic generation
264 | // The write response and response valid signals are asserted by the slave
265 | // when axi_wready, S_AXI_WVALID, axi_wready and S_AXI_WVALID are asserted.
266 | // This marks the acceptance of address and indicates the status of
267 | // write transaction.
268 |
269 | always @( posedge S_AXI_ACLK )
270 | begin
271 | if ( S_AXI_ARESETN == 1'b0 )
272 | begin
273 | axi_bvalid <= 0;
274 | axi_bresp <= 2'b0;
275 | end
276 | else
277 | begin
278 | if (axi_awready && S_AXI_AWVALID && ~axi_bvalid && axi_wready && S_AXI_WVALID)
279 | begin
280 | // indicates a valid write response is available
281 | axi_bvalid <= 1'b1;
282 | axi_bresp <= 2'b0; // 'OKAY' response
283 | end // work error responses in future
284 | else
285 | begin
286 | if (S_AXI_BREADY && axi_bvalid)
287 | //check if bready is asserted while bvalid is high)
288 | //(there is a possibility that bready is always asserted high)
289 | begin
290 | axi_bvalid <= 1'b0;
291 | end
292 | end
293 | end
294 | end
295 |
296 | // Implement axi_arready generation
297 | // axi_arready is asserted for one S_AXI_ACLK clock cycle when
298 | // S_AXI_ARVALID is asserted. axi_awready is
299 | // de-asserted when reset (active low) is asserted.
300 | // The read address is also latched when S_AXI_ARVALID is
301 | // asserted. axi_araddr is reset to zero on reset assertion.
302 |
303 | always @( posedge S_AXI_ACLK )
304 | begin
305 | if ( S_AXI_ARESETN == 1'b0 )
306 | begin
307 | axi_arready <= 1'b0;
308 | axi_araddr <= 32'b0;
309 | end
310 | else
311 | begin
312 | if (~axi_arready && S_AXI_ARVALID)
313 | begin
314 | // indicates that the slave has acceped the valid read address
315 | axi_arready <= 1'b1;
316 | // Read address latching
317 | axi_araddr <= S_AXI_ARADDR;
318 | end
319 | else
320 | begin
321 | axi_arready <= 1'b0;
322 | end
323 | end
324 | end
325 |
326 | // Implement axi_arvalid generation
327 | // axi_rvalid is asserted for one S_AXI_ACLK clock cycle when both
328 | // S_AXI_ARVALID and axi_arready are asserted. The slave registers
329 | // data are available on the axi_rdata bus at this instance. The
330 | // assertion of axi_rvalid marks the validity of read data on the
331 | // bus and axi_rresp indicates the status of read transaction.axi_rvalid
332 | // is deasserted on reset (active low). axi_rresp and axi_rdata are
333 | // cleared to zero on reset (active low).
334 | always @( posedge S_AXI_ACLK )
335 | begin
336 | if ( S_AXI_ARESETN == 1'b0 )
337 | begin
338 | axi_rvalid <= 0;
339 | axi_rresp <= 0;
340 | end
341 | else
342 | begin
343 | if (axi_arready && S_AXI_ARVALID && ~axi_rvalid)
344 | begin
345 | // Valid read data is available at the read data bus
346 | axi_rvalid <= 1'b1;
347 | axi_rresp <= 2'b0; // 'OKAY' response
348 | end
349 | else if (axi_rvalid && S_AXI_RREADY)
350 | begin
351 | // Read data is accepted by the master
352 | axi_rvalid <= 1'b0;
353 | end
354 | end
355 | end
356 |
357 | // Implement memory mapped register select and read logic generation
358 | // Slave register read enable is asserted when valid address is available
359 | // and the slave is ready to accept the read address.
360 | assign slv_reg_rden = axi_arready & S_AXI_ARVALID & ~axi_rvalid;
361 | always @(*)
362 | begin
363 | if ( S_AXI_ARESETN == 1'b0 )
364 | begin
365 | reg_data_out <= 0;
366 | end
367 | else
368 | begin
369 | // Address decoding for reading registers
370 | case ( axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
371 | 2'h0 : reg_data_out <= load_reg;
372 | 2'h1 : reg_data_out <= compare_reg;
373 | 2'h2 : reg_data_out <= control_reg;
374 | 2'h3 : reg_data_out <= unused_reg;
375 | default : reg_data_out <= 0;
376 | endcase
377 | end
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_gen #(.WIDTH(C_S_AXI_DATA_WIDTH)) pwm (
401 | .clk(S_AXI_ACLK),
402 | .rst(S_AXI_ARESETN),
403 | .load(load_reg),
404 | .compare(compare_reg),
405 | .control(control_reg),
406 | .pwm_out(pwm_out)
407 | );
408 | // User logic ends
409 |
410 | endmodule
411 |
--------------------------------------------------------------------------------