Compare commits

..

53 Commits

Author SHA1 Message Date
1f1fe1dff0 Fix missing PWM clock wire 2025-12-03 02:17:57 -08:00
8a7ed7a61b Update RGB PWM IP source list 2025-12-03 01:37:26 -08:00
631f46f339 Modify RGB PWM IP to use enable pulser instead of clock divider 2025-12-03 01:36:41 -08:00
8ffe9271f2 Modify hardware implemented PWM controller to use enable pulser 2025-12-03 00:07:02 -08:00
26eb01a9e8 Remove redundant clk_out from enable pulser
Was just passing through
2025-12-03 00:04:40 -08:00
6b441b12ae Add clock enable input to pwm_core 2025-12-03 00:02:34 -08:00
31730fba46 Add clock enable pulser with testbench
Clock divider implemented in RTL does not get implemented properly when
used inside IP block. Instead, this module will pulse an enable pin at
the same intervals, which will be used by other modules instead of a
separate clock.
2025-12-02 23:56:01 -08:00
067e4c1f6a Fix incorrect oen pin in HW PWM controller 2025-12-02 21:44:53 -08:00
941cbdf2dc Update HW PWM controller src list 2025-12-02 21:33:53 -08:00
1c532bdd95 Add HW PWM controller top module 2025-12-02 21:33:38 -08:00
616166f69f Add project for HW implementation of PWM controller 2025-12-02 21:19:16 -08:00
d85f2cf5d8 Updtae block design 2025-11-19 16:16:33 -08:00
ee6c3d2e60 Fix typos 2025-11-19 16:06:20 -08:00
ca29b6c586 Update block diagram, make external 2025-11-19 15:57:04 -08:00
72b06dbcb4 Add clock divider functionality to PWM AXI 2025-11-19 15:52:56 -08:00
e683fa28c8 Add power of two divider 2025-11-19 15:50:34 -08:00
ae5dcf1a4d Add BD to pwm_controller source list 2025-11-19 15:48:08 -08:00
5094eba511 Remove AXI constraints 2025-11-19 15:47:59 -08:00
904b083688 Add ZYNQ and AXI peripherals to BD 2025-11-19 15:47:48 -08:00
b33383ce5f Add pwm_controller block design 2025-11-19 15:38:36 -08:00
9f17c3f16f Update BD directory in .gitignore 2025-11-19 15:38:26 -08:00
a58148a92e Add RGB PWM AXI ip configuration 2025-11-19 15:31:47 -08:00
a2c0c9a972 Add PWM axi to list 2025-11-19 15:31:39 -08:00
89fc1846d5 Add RGB PWM AXI interface hdl 2025-11-19 15:31:04 -08:00
67bcca2aca Remove PWM block top testbench 2025-11-19 15:04:37 -08:00
b32ab40b0f Add PWM core testbench sim list 2025-11-19 14:59:48 -08:00
64f63e41b7 Fix PWM core off by one error, and missing posedge(rst) 2025-11-19 14:58:31 -08:00
002d2c0c06 Implement PWM core testbench 2025-11-19 14:57:40 -08:00
a352ed794c Add PWM core to project list 2025-11-19 13:33:43 -08:00
2695b69b21 Add PWM core testbench module 2025-11-19 13:24:13 -08:00
6418f366b3 Implement PWM core 2025-11-19 13:22:29 -08:00
7c2de42e2c Add pwm_core module 2025-11-19 13:12:24 -08:00
9d02715cb9 Add UserIP directory to pwm_controller config 2025-11-19 13:11:21 -08:00
5421c7ccc9 Modify package IP to delete temp project when finished 2025-11-11 00:09:33 -08:00
53aee5878e Remove frequency from ip configuration 2025-11-11 00:08:48 -08:00
cff5b413e7 Rename memory map name to be more descriptive 2025-11-11 00:08:35 -08:00
ca6826d53d Don't load customization parameters from ip.conf, keep auto 2025-11-11 00:07:05 -08:00
cf43ac49d3 Implement parsing memory map from ip.conf 2025-11-11 00:06:17 -08:00
49723910d8 Fix DictValueOr default value returning "default" string 2025-11-10 23:59:19 -08:00
e42d640083 Refactor IP parameter and port mapping into separate proc 2025-11-10 23:14:37 -08:00
51f3102fae Implement port mappings reading from ip.conf 2025-11-10 22:49:42 -08:00
68c9947d1a Fix IP interface not being set correctly
Need to set both bus abstraction and type, and they don't have the same
values
2025-11-10 22:48:55 -08:00
712206a524 Rename MAP to PORT in ip.conf 2025-11-10 22:48:14 -08:00
913af7a46a Partially implement manual IP configuration, user parameter config 2025-11-10 20:43:29 -08:00
600eebbd11 Change IP packaging configuration to use manual clk config 2025-11-10 20:42:47 -08:00
a6e86c8f3c Implement interface inferring 2025-11-10 19:50:08 -08:00
c2e6cec4e7 Add AXI interfaces to led_controller IP packaging 2025-11-10 19:49:45 -08:00
46c000b94a Package IP and clear settings in package_ip.tcl 2025-11-10 18:51:30 -08:00
bdc55177cd Add script for packaging IP from project
Currently just reads the IP configuration
2025-11-10 14:13:31 -08:00
c242ff9445 Add IP package configuration file to led_controller 2025-11-10 14:11:35 -08:00
8524f3908f Add led_controller Hog configuration 2025-11-09 22:18:01 -08:00
ee3c5dca87 Add LED controller AXI controller for testing 2025-11-09 22:15:56 -08:00
50853a7762 Add pwm_block project 2025-11-09 20:52:31 -08:00
32 changed files with 3027 additions and 3 deletions

4
.gitignore vendored
View File

@ -21,8 +21,8 @@
IP/*/* IP/*/*
!IP/*/*.xci* !IP/*/*.xci*
BD/* BD/*/*
!BD/*.bd !BD/*/*.bd
## OS ## OS
.DS_Store .DS_Store

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,6 @@
#vivado 2022.1
[main]
PART = xc7z007sclg400-1
IP_REPO_PATHS = "IP/ UserIP/"

View File

@ -0,0 +1,2 @@
pwm_controller/con/blackboard.xdc

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,4 @@
hw_pwm_controller/src/top.v top=top
pwm_block/src/pwm_core.v
pwm_block/src/clk_enable_pulser.v

View File

@ -0,0 +1,5 @@
#vivado 2022.1
[main]
PART = xc7z007sclg400-1

View File

@ -0,0 +1,31 @@
[main]
ROOT=UserIP/led_controller
VENDOR=user.org
LIBRARY=user
# What folder the IP shows up under in Vivado IPI
TAXONOMY=/UserIP
[interface.s_axi]
INTERFACE=xilinx.com:interface:aximm_rtl:1.0
INFER=true
PORTS=s_axi_awaddr s_axi_awprot s_axi_awvalid s_axi_awready s_axi_wdata s_axi_wstrb s_axi_wvalid s_axi_wready s_axi_bresp s_axi_bvalid s_axi_bready s_axi_araddr s_axi_arprot s_axi_arvalid s_axi_arready s_axi_rdata s_axi_rresp s_axi_rvalid s_axi_rready
[interface.s_axi_aclk]
INTERFACE=xilinx.com:signal:clock_rtl:1.0
ENABLEMENT_DEPENDENCY
INFER=false
MODE=slave
PORT.CLK=s_axi_aclk
PARAMETER.ASSOCIATED_RESET=s_axi_aresetn
PARAMETER.ASSOCIATED_BUSIF=s_axi
[interface.s_axi_aresetn]
INTERFACE=xilinx.com:signal:reset_rtl:1.0
INFER=true
PORTS=s_axi_aresetn
[memmap.s_axi]
BLOCKS=reg_base
BLOCK.reg_base.BASE_ADDRESS=0
BLOCK.reg_base.RANGE=32

View File

@ -0,0 +1,3 @@
led_controller/src/blackboard_led_controller_v1_0.v
led_controller/src/blackboard_led_controller_v1_0_S_AXI.v

5
Top/pwm_block/hog.conf Normal file
View File

@ -0,0 +1,5 @@
#vivado 2022.1
[main]
PART = xc7z007sclg400-1

31
Top/pwm_block/ip.conf Normal file
View File

@ -0,0 +1,31 @@
[main]
ROOT=UserIP/rgb_pwm_block
VENDOR=user.org
LIBRARY=user
# What folder the IP shows up under in Vivado IPI
TAXONOMY=/UserIP
[interface.s_axi]
INTERFACE=xilinx.com:interface:aximm_rtl:1.0
INFER=true
PORTS=s_axi_awaddr s_axi_awprot s_axi_awvalid s_axi_awready s_axi_wdata s_axi_wstrb s_axi_wvalid s_axi_wready s_axi_bresp s_axi_bvalid s_axi_bready s_axi_araddr s_axi_arprot s_axi_arvalid s_axi_arready s_axi_rdata s_axi_rresp s_axi_rvalid s_axi_rready
[interface.s_axi_aclk]
INTERFACE=xilinx.com:signal:clock_rtl:1.0
ENABLEMENT_DEPENDENCY
INFER=false
MODE=slave
PORT.CLK=s_axi_aclk
PARAMETER.ASSOCIATED_RESET=s_axi_aresetn
PARAMETER.ASSOCIATED_BUSIF=s_axi
[interface.s_axi_aresetn]
INTERFACE=xilinx.com:signal:reset_rtl:1.0
INFER=true
PORTS=s_axi_aresetn
[memmap.s_axi]
BLOCKS=reg_base
BLOCK.reg_base.BASE_ADDRESS=0
BLOCK.reg_base.RANGE=32

View File

@ -0,0 +1,12 @@
# Simulator xsim
[generics]
VCD_DUMPFILE=clk_enable_pulser_tb.vcd
[properties]
ACTIVE=1
TOP=clk_enable_pulser_tb
[files]
pwm_block/src/clk_enable_pulser.v
pwm_block/sim/clk_enable_pulser_tb.v

View File

@ -0,0 +1,2 @@
pwm_block/con/pwm_block.xdc

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,5 @@
pwm_block/src/rgb_pwm_block.v top=rgb_pwm_block
pwm_block/src/rgb_pwm_block_S_AXI.v
pwm_block/src/pwm_core.v
pwm_block/src/clk_enable_pulser.v

View File

@ -0,0 +1,12 @@
# Simulator xsim
[generics]
VCD_DUMPFILE=pwm_core_tb.vcd
[properties]
ACTIVE=1
TOP=pwm_core_tb
[files]
pwm_block/src/pwm_core.v
pwm_block/sim/pwm_core_tb.v

View File

@ -2,4 +2,5 @@
[main] [main]
PART = xc7z007sclg400-1 PART = xc7z007sclg400-1
IP_REPO_PATHS = "IP/ UserIP/"

View File

@ -1,2 +1,2 @@
pwm_controller/src/top.v top=top BD/pwm_controller/pwm_controller.bd top=pwm_controller

View File

@ -0,0 +1,41 @@
`timescale 1ns/1ps
module top (
input clk,
input [0:0] btn,
input [11:0] sw,
output [2:0] RGB_led_A,
output [7:0] led
);
wire pulse_en;
clk_enable_pulser enpulser (
.clk(clk),
.rst(0),
.sel(6),
.en_out(pulse_en)
);
wire cur_en = btn[0] ? pulse_en : 1;
wire oen = sw[11];
assign led[0] = oen;
pwm_core #(
.WINDOW_REG_SIZE(8)
) pwm_core_r (
.clk(clk),
.en(cur_en),
.rst(0),
.duty(sw[7:0]),
.window_width(255),
.oen(oen),
.pulse(RGB_led_A[0])
);
assign RGB_led_A[1] = 0;
assign RGB_led_A[2] = 0;
endmodule

View File

@ -0,0 +1,79 @@
`timescale 1 ns / 1 ps
module blackboard_led_controller_v1_0 #
(
// Users to add parameters here
// User parameters ends
// Do not modify the parameters beyond this line
// Parameters of Axi Slave Bus Interface S_AXI
parameter integer C_S_AXI_DATA_WIDTH = 32,
parameter integer C_S_AXI_ADDR_WIDTH = 4
)
(
// Users to add ports here
output wire [7:0] led,
// User ports ends
// Do not modify the ports beyond this line
// Ports of Axi Slave Bus Interface S_AXI
input wire s_axi_aclk,
input wire s_axi_aresetn,
input wire [C_S_AXI_ADDR_WIDTH-1 : 0] s_axi_awaddr,
input wire [2 : 0] s_axi_awprot,
input wire s_axi_awvalid,
output wire s_axi_awready,
input wire [C_S_AXI_DATA_WIDTH-1 : 0] s_axi_wdata,
input wire [(C_S_AXI_DATA_WIDTH/8)-1 : 0] s_axi_wstrb,
input wire s_axi_wvalid,
output wire s_axi_wready,
output wire [1 : 0] s_axi_bresp,
output wire s_axi_bvalid,
input wire s_axi_bready,
input wire [C_S_AXI_ADDR_WIDTH-1 : 0] s_axi_araddr,
input wire [2 : 0] s_axi_arprot,
input wire s_axi_arvalid,
output wire s_axi_arready,
output wire [C_S_AXI_DATA_WIDTH-1 : 0] s_axi_rdata,
output wire [1 : 0] s_axi_rresp,
output wire s_axi_rvalid,
input wire s_axi_rready
);
// Instantiation of Axi Bus Interface S_AXI
blackboard_led_controller_v1_0_S_AXI # (
.C_S_AXI_DATA_WIDTH(C_S_AXI_DATA_WIDTH),
.C_S_AXI_ADDR_WIDTH(C_S_AXI_ADDR_WIDTH)
) blackboard_led_controller_v1_0_S_AXI_inst (
.led(led),
.S_AXI_ACLK(s_axi_aclk),
.S_AXI_ARESETN(s_axi_aresetn),
.S_AXI_AWADDR(s_axi_awaddr),
.S_AXI_AWPROT(s_axi_awprot),
.S_AXI_AWVALID(s_axi_awvalid),
.S_AXI_AWREADY(s_axi_awready),
.S_AXI_WDATA(s_axi_wdata),
.S_AXI_WSTRB(s_axi_wstrb),
.S_AXI_WVALID(s_axi_wvalid),
.S_AXI_WREADY(s_axi_wready),
.S_AXI_BRESP(s_axi_bresp),
.S_AXI_BVALID(s_axi_bvalid),
.S_AXI_BREADY(s_axi_bready),
.S_AXI_ARADDR(s_axi_araddr),
.S_AXI_ARPROT(s_axi_arprot),
.S_AXI_ARVALID(s_axi_arvalid),
.S_AXI_ARREADY(s_axi_arready),
.S_AXI_RDATA(s_axi_rdata),
.S_AXI_RRESP(s_axi_rresp),
.S_AXI_RVALID(s_axi_rvalid),
.S_AXI_RREADY(s_axi_rready)
);
// Add user logic here
// User logic ends
endmodule

View File

@ -0,0 +1,407 @@
`timescale 1 ns / 1 ps
module blackboard_led_controller_v1_0_S_AXI #
(
// Users to add parameters here
// User parameters ends
// Do not modify the parameters beyond this line
// Width of S_AXI data bus
parameter integer C_S_AXI_DATA_WIDTH = 32,
// Width of S_AXI address bus
parameter integer C_S_AXI_ADDR_WIDTH = 4
)
(
// Users to add ports here
output wire [7:0] led,
// User ports ends
// Do not modify the ports beyond this line
// Global Clock Signal
input wire S_AXI_ACLK,
// Global Reset Signal. This Signal is Active LOW
input wire S_AXI_ARESETN,
// Write address (issued by master, acceped by Slave)
input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_AWADDR,
// Write channel Protection type. This signal indicates the
// privilege and security level of the transaction, and whether
// the transaction is a data access or an instruction access.
input wire [2 : 0] S_AXI_AWPROT,
// Write address valid. This signal indicates that the master signaling
// valid write address and control information.
input wire S_AXI_AWVALID,
// Write address ready. This signal indicates that the slave is ready
// to accept an address and associated control signals.
output wire S_AXI_AWREADY,
// Write data (issued by master, acceped by Slave)
input wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_WDATA,
// Write strobes. This signal indicates which byte lanes hold
// valid data. There is one write strobe bit for each eight
// bits of the write data bus.
input wire [(C_S_AXI_DATA_WIDTH/8)-1 : 0] S_AXI_WSTRB,
// Write valid. This signal indicates that valid write
// data and strobes are available.
input wire S_AXI_WVALID,
// Write ready. This signal indicates that the slave
// can accept the write data.
output wire S_AXI_WREADY,
// Write response. This signal indicates the status
// of the write transaction.
output wire [1 : 0] S_AXI_BRESP,
// Write response valid. This signal indicates that the channel
// is signaling a valid write response.
output wire S_AXI_BVALID,
// Response ready. This signal indicates that the master
// can accept a write response.
input wire S_AXI_BREADY,
// Read address (issued by master, acceped by Slave)
input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_ARADDR,
// Protection type. This signal indicates the privilege
// and security level of the transaction, and whether the
// transaction is a data access or an instruction access.
input wire [2 : 0] S_AXI_ARPROT,
// Read address valid. This signal indicates that the channel
// is signaling valid read address and control information.
input wire S_AXI_ARVALID,
// Read address ready. This signal indicates that the slave is
// ready to accept an address and associated control signals.
output wire S_AXI_ARREADY,
// Read data (issued by slave)
output wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_RDATA,
// Read response. This signal indicates the status of the
// read transfer.
output wire [1 : 0] S_AXI_RRESP,
// Read valid. This signal indicates that the channel is
// signaling the required read data.
output wire S_AXI_RVALID,
// Read ready. This signal indicates that the master can
// accept the read data and response information.
input wire S_AXI_RREADY
);
// AXI4LITE signals
reg [C_S_AXI_ADDR_WIDTH-1 : 0] axi_awaddr;
reg axi_awready;
reg axi_wready;
reg [1 : 0] axi_bresp;
reg axi_bvalid;
reg [C_S_AXI_ADDR_WIDTH-1 : 0] axi_araddr;
reg axi_arready;
reg [C_S_AXI_DATA_WIDTH-1 : 0] axi_rdata;
reg [1 : 0] axi_rresp;
reg axi_rvalid;
// Example-specific design signals
// local parameter for addressing 32 bit / 64 bit C_S_AXI_DATA_WIDTH
// ADDR_LSB is used for addressing 32/64 bit registers/memories
// ADDR_LSB = 2 for 32 bits (n downto 2)
// ADDR_LSB = 3 for 64 bits (n downto 3)
localparam integer ADDR_LSB = (C_S_AXI_DATA_WIDTH/32) + 1;
localparam integer OPT_MEM_ADDR_BITS = 1;
//----------------------------------------------
//-- Signals for user logic register space example
//------------------------------------------------
//-- Number of Slave Registers 4
reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg0;
reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg1;
reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg2;
reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg3;
wire slv_reg_rden;
wire slv_reg_wren;
reg [C_S_AXI_DATA_WIDTH-1:0] reg_data_out;
integer byte_index;
reg aw_en;
// I/O Connections assignments
assign S_AXI_AWREADY = axi_awready;
assign S_AXI_WREADY = axi_wready;
assign S_AXI_BRESP = axi_bresp;
assign S_AXI_BVALID = axi_bvalid;
assign S_AXI_ARREADY = axi_arready;
assign S_AXI_RDATA = axi_rdata;
assign S_AXI_RRESP = axi_rresp;
assign S_AXI_RVALID = axi_rvalid;
// Implement axi_awready generation
// axi_awready is asserted for one S_AXI_ACLK clock cycle when both
// S_AXI_AWVALID and S_AXI_WVALID are asserted. axi_awready is
// de-asserted when reset is low.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_awready <= 1'b0;
aw_en <= 1'b1;
end
else
begin
if (~axi_awready && S_AXI_AWVALID && S_AXI_WVALID && aw_en)
begin
// slave is ready to accept write address when
// there is a valid write address and write data
// on the write address and data bus. This design
// expects no outstanding transactions.
axi_awready <= 1'b1;
aw_en <= 1'b0;
end
else if (S_AXI_BREADY && axi_bvalid)
begin
aw_en <= 1'b1;
axi_awready <= 1'b0;
end
else
begin
axi_awready <= 1'b0;
end
end
end
// Implement axi_awaddr latching
// This process is used to latch the address when both
// S_AXI_AWVALID and S_AXI_WVALID are valid.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_awaddr <= 0;
end
else
begin
if (~axi_awready && S_AXI_AWVALID && S_AXI_WVALID && aw_en)
begin
// Write Address latching
axi_awaddr <= S_AXI_AWADDR;
end
end
end
// Implement axi_wready generation
// axi_wready is asserted for one S_AXI_ACLK clock cycle when both
// S_AXI_AWVALID and S_AXI_WVALID are asserted. axi_wready is
// de-asserted when reset is low.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_wready <= 1'b0;
end
else
begin
if (~axi_wready && S_AXI_WVALID && S_AXI_AWVALID && aw_en )
begin
// slave is ready to accept write data when
// there is a valid write address and write data
// on the write address and data bus. This design
// expects no outstanding transactions.
axi_wready <= 1'b1;
end
else
begin
axi_wready <= 1'b0;
end
end
end
// Implement memory mapped register select and write logic generation
// The write data is accepted and written to memory mapped registers when
// axi_awready, S_AXI_WVALID, axi_wready and S_AXI_WVALID are asserted. Write strobes are used to
// select byte enables of slave registers while writing.
// These registers are cleared when reset (active low) is applied.
// Slave register write enable is asserted when valid address and data are available
// and the slave is ready to accept the write address and write data.
assign slv_reg_wren = axi_wready && S_AXI_WVALID && axi_awready && S_AXI_AWVALID;
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
slv_reg0 <= 0;
slv_reg1 <= 0;
slv_reg2 <= 0;
slv_reg3 <= 0;
end
else begin
if (slv_reg_wren)
begin
case ( axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
2'h0:
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
if ( S_AXI_WSTRB[byte_index] == 1 ) begin
// Respective byte enables are asserted as per write strobes
// Slave register 0
slv_reg0[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
end
2'h1:
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
if ( S_AXI_WSTRB[byte_index] == 1 ) begin
// Respective byte enables are asserted as per write strobes
// Slave register 1
slv_reg1[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
end
2'h2:
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
if ( S_AXI_WSTRB[byte_index] == 1 ) begin
// Respective byte enables are asserted as per write strobes
// Slave register 2
slv_reg2[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
end
2'h3:
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
if ( S_AXI_WSTRB[byte_index] == 1 ) begin
// Respective byte enables are asserted as per write strobes
// Slave register 3
slv_reg3[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
end
default : begin
slv_reg0 <= slv_reg0;
slv_reg1 <= slv_reg1;
slv_reg2 <= slv_reg2;
slv_reg3 <= slv_reg3;
end
endcase
end
end
end
// Implement write response logic generation
// The write response and response valid signals are asserted by the slave
// when axi_wready, S_AXI_WVALID, axi_wready and S_AXI_WVALID are asserted.
// This marks the acceptance of address and indicates the status of
// write transaction.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_bvalid <= 0;
axi_bresp <= 2'b0;
end
else
begin
if (axi_awready && S_AXI_AWVALID && ~axi_bvalid && axi_wready && S_AXI_WVALID)
begin
// indicates a valid write response is available
axi_bvalid <= 1'b1;
axi_bresp <= 2'b0; // 'OKAY' response
end // work error responses in future
else
begin
if (S_AXI_BREADY && axi_bvalid)
//check if bready is asserted while bvalid is high)
//(there is a possibility that bready is always asserted high)
begin
axi_bvalid <= 1'b0;
end
end
end
end
// Implement axi_arready generation
// axi_arready is asserted for one S_AXI_ACLK clock cycle when
// S_AXI_ARVALID is asserted. axi_awready is
// de-asserted when reset (active low) is asserted.
// The read address is also latched when S_AXI_ARVALID is
// asserted. axi_araddr is reset to zero on reset assertion.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_arready <= 1'b0;
axi_araddr <= 32'b0;
end
else
begin
if (~axi_arready && S_AXI_ARVALID)
begin
// indicates that the slave has acceped the valid read address
axi_arready <= 1'b1;
// Read address latching
axi_araddr <= S_AXI_ARADDR;
end
else
begin
axi_arready <= 1'b0;
end
end
end
// Implement axi_arvalid generation
// axi_rvalid is asserted for one S_AXI_ACLK clock cycle when both
// S_AXI_ARVALID and axi_arready are asserted. The slave registers
// data are available on the axi_rdata bus at this instance. The
// assertion of axi_rvalid marks the validity of read data on the
// bus and axi_rresp indicates the status of read transaction.axi_rvalid
// is deasserted on reset (active low). axi_rresp and axi_rdata are
// cleared to zero on reset (active low).
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_rvalid <= 0;
axi_rresp <= 0;
end
else
begin
if (axi_arready && S_AXI_ARVALID && ~axi_rvalid)
begin
// Valid read data is available at the read data bus
axi_rvalid <= 1'b1;
axi_rresp <= 2'b0; // 'OKAY' response
end
else if (axi_rvalid && S_AXI_RREADY)
begin
// Read data is accepted by the master
axi_rvalid <= 1'b0;
end
end
end
// Implement memory mapped register select and read logic generation
// Slave register read enable is asserted when valid address is available
// and the slave is ready to accept the read address.
assign slv_reg_rden = axi_arready & S_AXI_ARVALID & ~axi_rvalid;
always @(*)
begin
// Address decoding for reading registers
case ( axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
2'h0 : reg_data_out <= slv_reg0;
2'h1 : reg_data_out <= slv_reg1;
2'h2 : reg_data_out <= slv_reg2;
2'h3 : reg_data_out <= slv_reg3;
default : reg_data_out <= 0;
endcase
end
// Output register or memory read data
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_rdata <= 0;
end
else
begin
// When there is a valid read address (S_AXI_ARVALID) with
// acceptance of read address by the slave (axi_arready),
// output the read dada
if (slv_reg_rden)
begin
axi_rdata <= reg_data_out; // register read data
end
end
end
// Add user logic here
wire led_en;
assign led_en = slv_reg1[0];
assign led = led_en ? slv_reg0[7:0] : 0;
// User logic ends
endmodule

View File

@ -0,0 +1,2 @@
# No constraints

View File

@ -0,0 +1,58 @@
`timescale 1ns/1ps
module clk_enable_pulser_tb #(
VCD_DUMPFILE = ""
);
reg clk = 0;
reg rst = 1;
reg [3:0] sel;
wire clk_out;
wire en_out;
clk_enable_pulser cut (
.clk(clk),
.rst(rst),
.sel(sel),
.clk_out(clk_out),
.en_out(en_out)
);
reg [4:0] test_counter = 0;
always @ (posedge clk) begin
if (en_out) begin
test_counter <= test_counter + 1;
end
end
integer k;
initial begin
rst = 1;
clk = 0;
#10
rst = 0;
sel = 2;
for (k=0; k<15; k=k+1) begin
#5 clk = 1;
#5 clk = 0;
end
sel = 3;
for (k=0; k<31; k=k+1) begin
#5 clk = 1;
#5 clk = 0;
end
$dumpvars;
$finish;
end
endmodule

View File

@ -0,0 +1,74 @@
`timescale 1ns/1ps
module pwm_core_tb #(
VCD_DUMPFILE = ""
);
reg clk = 0;
reg en = 1;
reg rst = 1;
reg [15:0] duty;
reg [15:0] window_width;
reg oen = 1;
wire pulse;
pwm_core#(
.WINDOW_REG_SIZE(16)
) cut (
.clk(clk),
.en(en)
.rst(rst),
.duty(duty),
.window_width(window_width),
.oen(oen),
.pulse(pulse)
);
integer k;
initial begin
rst = 1;
clk = 0;
#10
rst = 0;
oen = 1;
duty = 3;
window_width = 7;
for (k=0; k<13; k=k+1) begin
#5 clk = 1;
#5 clk = 0;
end
duty = 1;
for (k=0; k<13; k=k+1) begin
#5 clk = 1;
#5 clk = 0;
end
duty = 1;
window_width = 2;
for (k=0; k<8; k=k+1) begin
#5 clk = 1;
#5 clk = 0;
end
oen = 0;
for (k=0; k<5; k=k+1) begin
#5 clk = 1;
#5 clk = 0;
end
$dumpvars;
$finish;
end
endmodule

View File

@ -0,0 +1,19 @@
module clk_enable_pulser (
input clk,
input rst,
input [3:0] sel,
output wire en_out
);
reg [15:0] count = 0;
wire [15:0] trigger_bits = count >> sel;
always @ (posedge clk) begin
if (rst || trigger_bits) count <= 1;
else count <= count + 1;
end
assign en_out = count[sel];
endmodule

25
pwm_block/src/conf_div.v Normal file
View File

@ -0,0 +1,25 @@
//power-of-2 clock divider
//output clock frequency can be chosen through the input 'sel'
//output clock freqeuncy is clk_in/(2^(sel+1))
module conf_div (
input clk_in,
input rst,
input [3:0] sel,
output reg clk_out
);
wire cl_sel;
reg [15:0] count;
assign cl_sel = count[sel];
always @(posedge clk_in)
if(rst)
count<=0;
else
count<=count+1;
always @(posedge clk_in)
clk_out <= cl_sel;
endmodule

44
pwm_block/src/pwm_core.v Normal file
View File

@ -0,0 +1,44 @@
/*
* @brief PWM controller for a single output
*
* @param WINDOW_REG_SIZE Window and duty register size in bits
*
* @param [in] clk PWM counter clock
* @param [in] en Clock enable
* @param [in] rst Asynchronous reset
* @param [in] duty Duty cycle high time in number of clock cycles
* @param [in] window_width Number of clock cycles in a window
* @param [in] oen Output pulse enable
* @param [out] pulse Output pulse
*/
module pwm_core #(
WINDOW_REG_SIZE = 16
)(
input clk,
input en,
input rst,
input [WINDOW_REG_SIZE-1:0] duty,
input [WINDOW_REG_SIZE-1:0] window_width,
input oen,
output pulse
);
reg [WINDOW_REG_SIZE-1:0] duty_counter;
always @ (posedge(clk), posedge(rst)) begin
if (rst) duty_counter <= 0;
else if (en) begin
if (duty_counter >= window_width - 1)
duty_counter <= 0;
else
duty_counter <= duty_counter + 1;
end
end
wire pulse_high;
assign pulse_high = (duty_counter < duty) ? 1'b1 : 1'b0;
assign pulse = oen ? pulse_high : 1'b0;
endmodule

View File

@ -0,0 +1,79 @@
`timescale 1 ns / 1 ps
module rgb_pwm_block #
(
// Users to add parameters here
// User parameters ends
// Do not modify the parameters beyond this line
// Parameters of Axi Slave Bus Interface S_AXI
parameter integer C_S_AXI_DATA_WIDTH = 32,
parameter integer C_S_AXI_ADDR_WIDTH = 4
)
(
// Users to add ports here
output wire [2:0] RGB_led,
// User ports ends
// Do not modify the ports beyond this line
// Ports of Axi Slave Bus Interface S_AXI
input wire s_axi_aclk,
input wire s_axi_aresetn,
input wire [C_S_AXI_ADDR_WIDTH-1 : 0] s_axi_awaddr,
input wire [2 : 0] s_axi_awprot,
input wire s_axi_awvalid,
output wire s_axi_awready,
input wire [C_S_AXI_DATA_WIDTH-1 : 0] s_axi_wdata,
input wire [(C_S_AXI_DATA_WIDTH/8)-1 : 0] s_axi_wstrb,
input wire s_axi_wvalid,
output wire s_axi_wready,
output wire [1 : 0] s_axi_bresp,
output wire s_axi_bvalid,
input wire s_axi_bready,
input wire [C_S_AXI_ADDR_WIDTH-1 : 0] s_axi_araddr,
input wire [2 : 0] s_axi_arprot,
input wire s_axi_arvalid,
output wire s_axi_arready,
output wire [C_S_AXI_DATA_WIDTH-1 : 0] s_axi_rdata,
output wire [1 : 0] s_axi_rresp,
output wire s_axi_rvalid,
input wire s_axi_rready
);
// Instantiation of Axi Bus Interface S_AXI
rgb_pwm_block_S_AXI # (
.C_S_AXI_DATA_WIDTH(C_S_AXI_DATA_WIDTH),
.C_S_AXI_ADDR_WIDTH(C_S_AXI_ADDR_WIDTH)
) rgb_pwm_block_S_AXI_inst (
.RGB_led(RGB_led),
.S_AXI_ACLK(s_axi_aclk),
.S_AXI_ARESETN(s_axi_aresetn),
.S_AXI_AWADDR(s_axi_awaddr),
.S_AXI_AWPROT(s_axi_awprot),
.S_AXI_AWVALID(s_axi_awvalid),
.S_AXI_AWREADY(s_axi_awready),
.S_AXI_WDATA(s_axi_wdata),
.S_AXI_WSTRB(s_axi_wstrb),
.S_AXI_WVALID(s_axi_wvalid),
.S_AXI_WREADY(s_axi_wready),
.S_AXI_BRESP(s_axi_bresp),
.S_AXI_BVALID(s_axi_bvalid),
.S_AXI_BREADY(s_axi_bready),
.S_AXI_ARADDR(s_axi_araddr),
.S_AXI_ARPROT(s_axi_arprot),
.S_AXI_ARVALID(s_axi_arvalid),
.S_AXI_ARREADY(s_axi_arready),
.S_AXI_RDATA(s_axi_rdata),
.S_AXI_RRESP(s_axi_rresp),
.S_AXI_RVALID(s_axi_rvalid),
.S_AXI_RREADY(s_axi_rready)
);
// Add user logic here
// User logic ends
endmodule

View File

@ -0,0 +1,456 @@
`timescale 1 ns / 1 ps
module rgb_pwm_block_S_AXI #
(
// Users to add parameters here
// User parameters ends
// Do not modify the parameters beyond this line
// Width of S_AXI data bus
parameter integer C_S_AXI_DATA_WIDTH = 32,
// Width of S_AXI address bus
parameter integer C_S_AXI_ADDR_WIDTH = 4
)
(
// Users to add ports here
output wire [2:0] RGB_led,
// User ports ends
// Do not modify the ports beyond this line
// Global Clock Signal
input wire S_AXI_ACLK,
// Global Reset Signal. This Signal is Active LOW
input wire S_AXI_ARESETN,
// Write address (issued by master, acceped by Slave)
input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_AWADDR,
// Write channel Protection type. This signal indicates the
// privilege and security level of the transaction, and whether
// the transaction is a data access or an instruction access.
input wire [2 : 0] S_AXI_AWPROT,
// Write address valid. This signal indicates that the master signaling
// valid write address and control information.
input wire S_AXI_AWVALID,
// Write address ready. This signal indicates that the slave is ready
// to accept an address and associated control signals.
output wire S_AXI_AWREADY,
// Write data (issued by master, acceped by Slave)
input wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_WDATA,
// Write strobes. This signal indicates which byte lanes hold
// valid data. There is one write strobe bit for each eight
// bits of the write data bus.
input wire [(C_S_AXI_DATA_WIDTH/8)-1 : 0] S_AXI_WSTRB,
// Write valid. This signal indicates that valid write
// data and strobes are available.
input wire S_AXI_WVALID,
// Write ready. This signal indicates that the slave
// can accept the write data.
output wire S_AXI_WREADY,
// Write response. This signal indicates the status
// of the write transaction.
output wire [1 : 0] S_AXI_BRESP,
// Write response valid. This signal indicates that the channel
// is signaling a valid write response.
output wire S_AXI_BVALID,
// Response ready. This signal indicates that the master
// can accept a write response.
input wire S_AXI_BREADY,
// Read address (issued by master, acceped by Slave)
input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_ARADDR,
// Protection type. This signal indicates the privilege
// and security level of the transaction, and whether the
// transaction is a data access or an instruction access.
input wire [2 : 0] S_AXI_ARPROT,
// Read address valid. This signal indicates that the channel
// is signaling valid read address and control information.
input wire S_AXI_ARVALID,
// Read address ready. This signal indicates that the slave is
// ready to accept an address and associated control signals.
output wire S_AXI_ARREADY,
// Read data (issued by slave)
output wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_RDATA,
// Read response. This signal indicates the status of the
// read transfer.
output wire [1 : 0] S_AXI_RRESP,
// Read valid. This signal indicates that the channel is
// signaling the required read data.
output wire S_AXI_RVALID,
// Read ready. This signal indicates that the master can
// accept the read data and response information.
input wire S_AXI_RREADY
);
// AXI4LITE signals
reg [C_S_AXI_ADDR_WIDTH-1 : 0] axi_awaddr;
reg axi_awready;
reg axi_wready;
reg [1 : 0] axi_bresp;
reg axi_bvalid;
reg [C_S_AXI_ADDR_WIDTH-1 : 0] axi_araddr;
reg axi_arready;
reg [C_S_AXI_DATA_WIDTH-1 : 0] axi_rdata;
reg [1 : 0] axi_rresp;
reg axi_rvalid;
// Example-specific design signals
// local parameter for addressing 32 bit / 64 bit C_S_AXI_DATA_WIDTH
// ADDR_LSB is used for addressing 32/64 bit registers/memories
// ADDR_LSB = 2 for 32 bits (n downto 2)
// ADDR_LSB = 3 for 64 bits (n downto 3)
localparam integer ADDR_LSB = (C_S_AXI_DATA_WIDTH/32) + 1;
localparam integer OPT_MEM_ADDR_BITS = 1;
//----------------------------------------------
//-- Signals for user logic register space example
//------------------------------------------------
//-- Number of Slave Registers 4
reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg0;
reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg1;
reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg2;
reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg3;
wire slv_reg_rden;
wire slv_reg_wren;
reg [C_S_AXI_DATA_WIDTH-1:0] reg_data_out;
integer byte_index;
reg aw_en;
// I/O Connections assignments
assign S_AXI_AWREADY = axi_awready;
assign S_AXI_WREADY = axi_wready;
assign S_AXI_BRESP = axi_bresp;
assign S_AXI_BVALID = axi_bvalid;
assign S_AXI_ARREADY = axi_arready;
assign S_AXI_RDATA = axi_rdata;
assign S_AXI_RRESP = axi_rresp;
assign S_AXI_RVALID = axi_rvalid;
// Implement axi_awready generation
// axi_awready is asserted for one S_AXI_ACLK clock cycle when both
// S_AXI_AWVALID and S_AXI_WVALID are asserted. axi_awready is
// de-asserted when reset is low.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_awready <= 1'b0;
aw_en <= 1'b1;
end
else
begin
if (~axi_awready && S_AXI_AWVALID && S_AXI_WVALID && aw_en)
begin
// slave is ready to accept write address when
// there is a valid write address and write data
// on the write address and data bus. This design
// expects no outstanding transactions.
axi_awready <= 1'b1;
aw_en <= 1'b0;
end
else if (S_AXI_BREADY && axi_bvalid)
begin
aw_en <= 1'b1;
axi_awready <= 1'b0;
end
else
begin
axi_awready <= 1'b0;
end
end
end
// Implement axi_awaddr latching
// This process is used to latch the address when both
// S_AXI_AWVALID and S_AXI_WVALID are valid.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_awaddr <= 0;
end
else
begin
if (~axi_awready && S_AXI_AWVALID && S_AXI_WVALID && aw_en)
begin
// Write Address latching
axi_awaddr <= S_AXI_AWADDR;
end
end
end
// Implement axi_wready generation
// axi_wready is asserted for one S_AXI_ACLK clock cycle when both
// S_AXI_AWVALID and S_AXI_WVALID are asserted. axi_wready is
// de-asserted when reset is low.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_wready <= 1'b0;
end
else
begin
if (~axi_wready && S_AXI_WVALID && S_AXI_AWVALID && aw_en )
begin
// slave is ready to accept write data when
// there is a valid write address and write data
// on the write address and data bus. This design
// expects no outstanding transactions.
axi_wready <= 1'b1;
end
else
begin
axi_wready <= 1'b0;
end
end
end
// Implement memory mapped register select and write logic generation
// The write data is accepted and written to memory mapped registers when
// axi_awready, S_AXI_WVALID, axi_wready and S_AXI_WVALID are asserted. Write strobes are used to
// select byte enables of slave registers while writing.
// These registers are cleared when reset (active low) is applied.
// Slave register write enable is asserted when valid address and data are available
// and the slave is ready to accept the write address and write data.
assign slv_reg_wren = axi_wready && S_AXI_WVALID && axi_awready && S_AXI_AWVALID;
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
slv_reg0 <= 0;
slv_reg1 <= 0;
slv_reg2 <= 0;
slv_reg3 <= 0;
end
else begin
if (slv_reg_wren)
begin
case ( axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
2'h0:
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
if ( S_AXI_WSTRB[byte_index] == 1 ) begin
// Respective byte enables are asserted as per write strobes
// Slave register 0
slv_reg0[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
end
2'h1:
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
if ( S_AXI_WSTRB[byte_index] == 1 ) begin
// Respective byte enables are asserted as per write strobes
// Slave register 1
slv_reg1[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
end
2'h2:
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
if ( S_AXI_WSTRB[byte_index] == 1 ) begin
// Respective byte enables are asserted as per write strobes
// Slave register 2
slv_reg2[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
end
2'h3:
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
if ( S_AXI_WSTRB[byte_index] == 1 ) begin
// Respective byte enables are asserted as per write strobes
// Slave register 3
slv_reg3[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
end
default : begin
slv_reg0 <= slv_reg0;
slv_reg1 <= slv_reg1;
slv_reg2 <= slv_reg2;
slv_reg3 <= slv_reg3;
end
endcase
end
end
end
// Implement write response logic generation
// The write response and response valid signals are asserted by the slave
// when axi_wready, S_AXI_WVALID, axi_wready and S_AXI_WVALID are asserted.
// This marks the acceptance of address and indicates the status of
// write transaction.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_bvalid <= 0;
axi_bresp <= 2'b0;
end
else
begin
if (axi_awready && S_AXI_AWVALID && ~axi_bvalid && axi_wready && S_AXI_WVALID)
begin
// indicates a valid write response is available
axi_bvalid <= 1'b1;
axi_bresp <= 2'b0; // 'OKAY' response
end // work error responses in future
else
begin
if (S_AXI_BREADY && axi_bvalid)
//check if bready is asserted while bvalid is high)
//(there is a possibility that bready is always asserted high)
begin
axi_bvalid <= 1'b0;
end
end
end
end
// Implement axi_arready generation
// axi_arready is asserted for one S_AXI_ACLK clock cycle when
// S_AXI_ARVALID is asserted. axi_awready is
// de-asserted when reset (active low) is asserted.
// The read address is also latched when S_AXI_ARVALID is
// asserted. axi_araddr is reset to zero on reset assertion.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_arready <= 1'b0;
axi_araddr <= 32'b0;
end
else
begin
if (~axi_arready && S_AXI_ARVALID)
begin
// indicates that the slave has acceped the valid read address
axi_arready <= 1'b1;
// Read address latching
axi_araddr <= S_AXI_ARADDR;
end
else
begin
axi_arready <= 1'b0;
end
end
end
// Implement axi_arvalid generation
// axi_rvalid is asserted for one S_AXI_ACLK clock cycle when both
// S_AXI_ARVALID and axi_arready are asserted. The slave registers
// data are available on the axi_rdata bus at this instance. The
// assertion of axi_rvalid marks the validity of read data on the
// bus and axi_rresp indicates the status of read transaction.axi_rvalid
// is deasserted on reset (active low). axi_rresp and axi_rdata are
// cleared to zero on reset (active low).
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_rvalid <= 0;
axi_rresp <= 0;
end
else
begin
if (axi_arready && S_AXI_ARVALID && ~axi_rvalid)
begin
// Valid read data is available at the read data bus
axi_rvalid <= 1'b1;
axi_rresp <= 2'b0; // 'OKAY' response
end
else if (axi_rvalid && S_AXI_RREADY)
begin
// Read data is accepted by the master
axi_rvalid <= 1'b0;
end
end
end
// Implement memory mapped register select and read logic generation
// Slave register read enable is asserted when valid address is available
// and the slave is ready to accept the read address.
assign slv_reg_rden = axi_arready & S_AXI_ARVALID & ~axi_rvalid;
always @(*)
begin
// Address decoding for reading registers
case ( axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
2'h0 : reg_data_out <= slv_reg0;
2'h1 : reg_data_out <= slv_reg1;
2'h2 : reg_data_out <= slv_reg2;
2'h3 : reg_data_out <= slv_reg3;
default : reg_data_out <= 0;
endcase
end
// Output register or memory read data
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_rdata <= 0;
end
else
begin
// When there is a valid read address (S_AXI_ARVALID) with
// acceptance of read address by the slave (axi_arready),
// output the read dada
if (slv_reg_rden)
begin
axi_rdata <= reg_data_out; // register read data
end
end
end
// Add user logic here
wire rst = ~S_AXI_ARESETN;
wire [15:0] duty_R = slv_reg0[15:0];
wire [15:0] duty_G = slv_reg1[15:0];
wire [15:0] duty_B = slv_reg2[15:0];
wire [15:0] window_width = slv_reg3[31:16];
wire [3:0] pwm_clk_mod = slv_reg3[11:8];
wire pwm_oen = slv_reg3[0];
wire pwm_clk;
wire pwm_clk_en;
assign pwm_clk = S_AXI_ACLK;
clk_enable_pulser clkdiv_pulser (
.clk(pwm_clk),
.rst(rst),
.sel(pwm_clk_mod),
.en_out(pwm_clk_en)
);
pwm_core core_R(
.clk(pwm_clk),
.en(pwm_clk_en),
.rst(rst),
.duty(duty_R),
.window_width(window_width),
.oen(pwm_oen),
.pulse(RGB_led[0])
);
pwm_core core_G(
.clk(pwm_clk),
.en(pwm_clk_en),
.rst(rst),
.duty(duty_G),
.window_width(window_width),
.oen(pwm_oen),
.pulse(RGB_led[1])
);
pwm_core core_B(
.clk(pwm_clk),
.en(pwm_clk_en),
.rst(rst),
.duty(duty_B),
.window_width(window_width),
.oen(pwm_oen),
.pulse(RGB_led[2])
);
// User logic ends
endmodule

9
pwm_block/src/top.v Normal file
View File

@ -0,0 +1,9 @@
module top(
input clk,
output [7:0] led
);
assign led = 8'b11111101;
endmodule

15
scripts/package_ip.sh Executable file
View File

@ -0,0 +1,15 @@
#!/usr/bin/bash
THISSCRIPT=$(realpath $0)
PROJECT_ROOT=$(dirname $(dirname $THISSCRIPT))
TCLSCRIPT="$PROJECT_ROOT/tcl/package_ip.tcl"
if [ $# -lt 1 ]; then
echo "Usage: $0 PROJECT_NAME"
exit -1
fi
PROJNAME=$1
$VIVADO_ROOT/bin/vivado -mode batch -source "$TCLSCRIPT" -tclargs "$PROJNAME"

292
tcl/package_ip.tcl Normal file
View File

@ -0,0 +1,292 @@
proc DictValueOr { dict key default } {
if { [dict exists $dict $key] } {
return [dict get $dict $key]
} else {
return $default
}
}
# Returns the value after in the string after startword and a delimiter
#
# If the string's format does not match, returns empty string
#
# EX: [SplitSub foo.bar foo] => "bar"
# EX: [SplitSub foo.biz foo] => "biz"
# EX: [SplitSub foobar foo] => ""
# EX: [SplitSub buz.bar foo] => ""
proc SplitSub { str startword } {
if { [string first $startword $str] == 0 } {
set sepidx [string first "." "$str"]
if { $sepidx == -1 } {
return ""
}
set val [string range "$str" [expr $sepidx + 1] [string length "$str"]]
return $val
} else {
return ""
}
}
# Gets all the values whose key starts with 'startword.*', returns a
# dictionary with the same values, but the keys are instead '*'.
#
# EX: With the dictionary d = { param.foo one param.bar two biz three }
#
# Calling [GetSubValues $d "param"] would result in the dictionary:
#
# { foo one bar two }
proc GetSubValues {map subcat} {
set res_map [dict create]
foreach {key val} $map {
set new_key [SplitSub $key $subcat]
if { $new_key != "" } {
dict append res_map $new_key $val
}
}
return $res_map
}
proc InterfaceAddParameters {iface params} {
foreach {param_name val} $params {
puts "Setting parameter $param_name"
ipx::add_bus_parameter $param_name $iface
set param [ipx::get_bus_parameters $param_name -of_objects $iface]
set_property value $val $param
}
}
proc InterfaceAddPortMaps {iface port_maps} {
foreach {logical_port physical_port} $port_maps {
puts "Mapping logical port $logical_port"
ipx::add_port_map $logical_port $iface
set port_map [ipx::get_port_maps $logical_port -of_objects $iface]
set_property physical_name $physical_port $port_map
}
}
# Parse the interface configuration and create a new interface
# in the current core
#
# @param iface_name Name of the interface to crate
# @param iface_conf Dictionary containing the interface configuration, parsed
# from hog.tcl ReadConf
#
proc ParseInterfaceConf { iface_name iface_conf } {
puts "Parsing interface $iface_name"
set iface_abstraction [dict get $iface_conf "INTERFACE"]
variable infer false
if { [dict exists $iface_conf "INFER"] } {
set infer [dict get $iface_conf "INFER"]
}
# Creating interface object
if { $infer } {
puts "Inferring interface"
set ports [split [dict get $iface_conf "PORTS"]]
puts "Using ports: $ports"
set result [ipx::infer_bus_interface $ports $iface_abstraction [ipx::current_core]]
# Inferred name could differ from the desired name, so set it
set inferred_name [lindex $result 2]
set created_iface [ipx::get_bus_interfaces $inferred_name -of_objects [ipx::current_core]]
set_property NAME $iface_name $created_iface
} else {
puts "Manually creating interface"
ipx::add_bus_interface $iface_name [ipx::current_core]
set created_iface [ipx::get_bus_interfaces $iface_name -of_objects [ipx::current_core]]
set_property ABSTRACTION_TYPE_VLNV $iface_abstraction $created_iface
# We need to set BOTH the bus ABSTRACTION, and the bus TYPE.
# The type can be obtained from the abstraction, so it is what
# is provided in ip.conf. Vivado does not provide an easy way
# to get one from the other, so we load the interface IP and
# get it's property.
set bus_abstraction [lindex [ipx::get_ipfiles -type busabs $iface_abstraction] 0]
set bus_type_vlnv [get_property BUS_TYPE_VLNV $bus_abstraction]
set_property BUS_TYPE_VLNV $bus_type_vlnv $created_iface
}
set iface [ipx::get_bus_interfaces $iface_name -of_objects [ipx::current_core]]
# Set interface properties
set_property DESCRIPTION [DictValueOr $iface_conf "DESCRIPTION" ""] $iface
set_property DISPLAY_NAME [DictValueOr $iface_conf "DISPLAY_NAME" ""] $iface
# If no mode is explicitly specified, use the potentially inferred mode
if { [dict exists $iface_conf "MODE"] } {
set_property INTERFACE_MODE [dict get $iface_conf "MODE"] $iface
}
set params [GetSubValues $iface_conf "PARAMETER"]
set port_maps [GetSubValues $iface_conf "PORT"]
InterfaceAddParameters $iface $params
InterfaceAddPortMaps $iface $port_maps
}
proc AddMemoryMapBlock {memmap block_name block_conf} {
set disp_name [DictValueOr $block_conf "DISPLAY_NAME" ""]
set description [DictValueOr $block_conf "DESCRIPTION" ""]
set base_addr [DictValueOr $block_conf "BASE_ADDRESS" "0"]
set range [DictValueOr $block_conf "RANGE" "4096"]
set width [DictValueOr $block_conf "WIDTH" "0"]
set block [ipx::add_address_block $block_name $memmap]
set_property DISPLAY_NAME $disp_name $block
set_property DESCRIPTION $description $block
set_property BASE_ADDRESS $base_addr $block
set_property RANGE $range $block
set_property WIDTH $width $block
}
proc ParseMemoryMapConf {iface_name memmap_conf} {
set memmap_name "${iface_name}_mem"
set blocks [dict get $memmap_conf "BLOCKS"]
set memmap [ipx::add_memory_map $memmap_name [ipx::current_core]]
set associated_iface [ipx::get_bus_interfaces $iface_name -of_objects [ipx::current_core]]
set_property SLAVE_MEMORY_MAP_REF $memmap_name $associated_iface
foreach block_name $blocks {
puts "Parsing block $block_name of $memmap_name"
set block_conf [GetSubValues $memmap_conf $block_name]
AddMemoryMapBlock $memmap $block_name $block_conf
}
}
set gitroot [exec git rev-parse --show-toplevel]
set hogroot "$gitroot/Hog"
puts "Hog root: $hogroot"
puts "Sourcing hog.tcl"
# Using ReadConf
source -notrace "$hogroot/Tcl/hog.tcl"
if { $argc < 1 } {
puts "Usage: vivado -mode batch -source package_ip.tcl -tclargs PROJECT_NAME"
exit -1
}
set proj_name [lindex $argv 0]
set tmp_proj_name "tmp_$proj_name"
puts "Packaging $proj_name"
set proj_top_dir "$gitroot/Top/$proj_name"
set proj_dir "$gitroot/Projects/$proj_name"
set proj_file "$proj_dir/$proj_name.xpr"
set ip_conf_file "$proj_top_dir/ip.conf"
puts "Reading configuration file $ip_conf_file"
set ip_conf [ReadConf "$ip_conf_file"]
puts "IP Configuration: $ip_conf"
if { [dict exists $ip_conf main] == 0 } {
puts "No main section in IP configuration!"
exit -2
}
set ip_root "$gitroot/[dict get $ip_conf main ROOT]"
set ip_vendor [dict get $ip_conf main VENDOR]
set ip_library [dict get $ip_conf main LIBRARY]
set ip_taxonomy [dict get $ip_conf main TAXONOMY]
puts "Opening project $proj_file"
open_project "$proj_file"
puts "Packaging project"
ipx::package_project -root_dir "$ip_root" -vendor "$ip_vendor" -library "$ip_library" -taxonomy "$ip_taxonomy" -import_files -set_current false
set ip_component_file "$ip_root/component.xml"
puts "Unloading core"
ipx::unload_core "$ip_component_file"
ipx::edit_ip_in_project -name "$tmp_proj_name" -upgrade true -directory "$ip_root" "$ip_component_file"
update_compile_order -fileset sources_1
current_project "$tmp_proj_name"
# Remove inferred interfaces and memory maps. Start from a blank slate.
puts "Removing inferred interfaces"
foreach iface [ipx::get_bus_interfaces -of_objects [ipx::current_core]] {
set iface_name [lindex $iface 2]
puts "Removing inferred bus interface $iface_name"
ipx::remove_bus_interface $iface_name [ipx::current_core]
}
foreach memmap [ipx::get_memory_maps -of_objects [ipx::current_core]] {
set memmap_name [lindex $memmap 2]
puts "Removing inferred memory map $memmap_name"
ipx::remove_memory_map $memmap_name [ipx::current_core]
}
puts "Parsing interfaces"
foreach {key val} $ip_conf {
if { [string first "interface" "$key"] == 0 } {
set sepidx [string first "." "$key"]
set iface_name [string range "$key" [expr $sepidx + 1] [string length "$key"]]
ParseInterfaceConf "$iface_name" $val
} else {
puts "$key is not an interface"
}
}
puts "Parsing memory maps"
set memmaps [GetSubValues $ip_conf "memmap"]
puts "Memmaps: $memmaps"
foreach {associated_iface memmap_conf} $memmaps {
puts "Parsing memory map for iface $associated_iface, conf: $memmap_conf"
ParseMemoryMapConf $associated_iface $memmap_conf
}
puts "Packaging IP"
# Increment revision
set rev [get_property core_revision [ipx::current_core]]
incr rev
set_property core_revision $rev [ipx::current_core]
ipx::update_source_project_archive -component [ipx::current_core]
ipx::create_xgui_files [ipx::current_core]
ipx::update_checksums [ipx::current_core]
ipx::check_integrity [ipx::current_core]
ipx::save_core [ipx::current_core]
close_project -delete
puts "Done"