added nexys video makefile for openxc7 run

This commit is contained in:
AngeloJacobo 2024-10-17 18:15:13 +08:00
parent 848bd3b613
commit 06b6f8f085
12 changed files with 485 additions and 501 deletions

View File

@ -0,0 +1,70 @@
PROJECT = nexysvideo_ddr3
FAMILY = artix7
PART = xc7a200tsbg484-1
CHIPDB = ${ARTIX7_CHIPDB}
ADDITIONAL_SOURCES = ../../rtl/ddr3_controller.v ../../rtl/ddr3_phy.v ../../rtl/ddr3_top.v uart_rx.v uart_tx.v clk_wiz.v
#############################################################################################
NEXTPNR_XILINX_DIR ?= /snap/openxc7/current/opt/nextpnr-xilinx
NEXTPNR_XILINX_PYTHON_DIR ?= ${NEXTPNR_XILINX_DIR}/python
PRJXRAY_DB_DIR ?= ${NEXTPNR_XILINX_DIR}/external/prjxray-db
DBPART = $(shell echo ${PART} | sed -e 's/-[0-9]//g')
SPEEDGRADE = $(shell echo ${PART} | sed -e 's/.*\-\([0-9]\)/\1/g')
CHIPDB ?= ./
ifeq ($(CHIPDB),)
CHIPDB = ./
endif
PYPY3 ?= pypy3
TOP ?= ${PROJECT}
TOP_MODULE ?= ${TOP}
TOP_VERILOG ?= ${TOP}.v
PNR_DEBUG ?= # --verbose --debug
BOARD ?= UNKNOWN
JTAG_LINK ?= --board ${BOARD}
XDC ?= ${PROJECT}.xdc
.PHONY: all
all: ${PROJECT}.bit
.PHONY: program
program: ${PROJECT}.bit
openFPGALoader ${JTAG_LINK} --bitstream $<
${PROJECT}.json: ${TOP_VERILOG} ${ADDITIONAL_SOURCES}
yosys -p "synth_xilinx -flatten -abc9 ${SYNTH_OPTS} -arch xc7 -top ${TOP_MODULE}; write_json ${PROJECT}.json" $< ${ADDITIONAL_SOURCES}
# The chip database only needs to be generated once
# that is why we don't clean it with make clean
${CHIPDB}/${DBPART}.bin:
${PYPY3} ${NEXTPNR_XILINX_PYTHON_DIR}/bbaexport.py --device ${PART} --bba ${DBPART}.bba
bbasm -l ${DBPART}.bba ${CHIPDB}/${DBPART}.bin
rm -f ${DBPART}.bba
${PROJECT}.fasm: ${PROJECT}.json ${CHIPDB}/${DBPART}.bin ${XDC}
nextpnr-xilinx --chipdb ${CHIPDB}/${DBPART}.bin --xdc ${XDC} --json ${PROJECT}.json --fasm $@ ${PNR_ARGS} ${PNR_DEBUG}
${PROJECT}.frames: ${PROJECT}.fasm
fasm2frames --part ${PART} --db-root ${PRJXRAY_DB_DIR}/${FAMILY} $< > $@
${PROJECT}.bit: ${PROJECT}.frames
xc7frames2bit --part_file ${PRJXRAY_DB_DIR}/${FAMILY}/${PART}/part.yaml --part_name ${PART} --frm_file $< --output_file $@
.PHONY: clean
clean:
@rm -f *.bit
@rm -f *.frames
@rm -f *.fasm
@rm -f *.json
@rm -f *.bin
@rm -f *.bba
.PHONY: pnrclean
pnrclean:
rm *.fasm *.frames *.bit

View File

@ -1,50 +0,0 @@
# Generated by https://github.com/FPGAOL-CE/caas-wizard
DB_DIR = /nextpnr-xilinx/xilinx/external/prjxray-db
CHIPDB = ./chipdb
BUILDDIR := ${CURDIR}/build
TOP := nexysvideo_ddr3
#SOURCES := $(wildcard *.v ../../rtl/ddr3*.v)
XDC := $(wildcard $(wildcard Nexys-video.xdc) )
CHIPFAM := artix7
PART := xc7a200tsbg484-1
LOGFILE := ${BUILDDIR}/top.log
all: ${CHIPDB} ${BUILDDIR} ${BUILDDIR}/top.bit
${BUILDDIR}:
mkdir -m 777 -p ${BUILDDIR} && chown -R nobody ${BUILDDIR} | true
${CHIPDB}:
mkdir -m 777 -p ${CHIPDB} && chown -R nobody ${CHIPDB} | true
# we run this in parent directory to seeminglessly import user source files
# otherwise have to parse user pattern and add ../
${BUILDDIR}/top.json: $(wildcard *.v) $(wildcard ../../rtl/*.v)
yosys -p "synth_xilinx -flatten -abc9 -arch xc7 -top ${TOP}; write_json ${BUILDDIR}/top.json" $^ >> ${LOGFILE} 2>&1
# The chip database only needs to be generated once
# that is why we don't clean it with make clean
${CHIPDB}/${PART}.bin:
pypy3 /nextpnr-xilinx/xilinx/python/bbaexport.py --device ${PART} --bba ${PART}.bba
bbasm -l ${PART}.bba ${CHIPDB}/${PART}.bin
rm -f ${PART}.bba
${BUILDDIR}/top.fasm: ${BUILDDIR}/top.json ${CHIPDB}/${PART}.bin
nextpnr-xilinx --chipdb ${CHIPDB}/${PART}.bin --xdc ${XDC} --json ${BUILDDIR}/top.json --pre-place constraints.py --pre-route show_bels.py --fasm $@ >> ${LOGFILE} 2>&1
#nextpnr-xilinx --chipdb ${CHIPDB}/${PART}.bin --xdc ${XDC} --json ${BUILDDIR}/top.json --fasm $@ >> ${LOGFILE} 2>&1
${BUILDDIR}/top.frames: ${BUILDDIR}/top.fasm
fasm2frames --part ${PART} --db-root ${DB_DIR}/${CHIPFAM} $< > $@
${BUILDDIR}/top.bit: ${BUILDDIR}/top.frames
xc7frames2bit --part_file ${DB_DIR}/${CHIPFAM}/${PART}/part.yaml --part_name ${PART} --frm_file $< --output_file $@ >> ${LOGFILE} 2>&1
.PHONY: clean
clean:
@rm -f *.bit
@rm -f *.frames
@rm -f *.fasm
@rm -f *.json

View File

@ -1,44 +0,0 @@
# Generated by https://github.com/FPGAOL-CE/caas-wizard
BUILDDIR := ${CURDIR}/build
# TOP := nexysvideo_ddr3
# SOURCES := $(wildcard *.v ../../rtl/ddr3*.v)
# XDC := $(wildcard $(wildcard Nexys-video.xdc) $(wildcard Nexys-video-vivado.xdc) )
# PART := xc7a200tsbg484-1
LOGFILE := ${BUILDDIR}/top.log
# Build design
all: ${BUILDDIR}/top.bit
${BUILDDIR}:
mkdir -m 777 -p ${BUILDDIR} && chown -R nobody ${BUILDDIR} | true
.ONESHELL:
${BUILDDIR}/vivado.tcl: ${BUILDDIR}
cat << EOF > $@
# vivado.tcl generated for caas
# can be launched from any directory
cd ${BUILDDIR}
create_project -part xc7a200tsbg484-1 -force v_proj
set_property target_language Verilog [current_project]
cd ..
read_verilog [glob *.v ../../rtl/ddr3*.v]
read_xdc [glob $(wildcard Nexys-video.xdc) $(wildcard Nexys-video-vivado.xdc) ]
cd build
synth_design -top nexysvideo_ddr3
opt_design
place_design
phys_opt_design
route_design
write_bitstream -verbose -force top.bit
# report_utilization -file util.rpt
# report_timing_summary -file timing.rpt
EOF
${BUILDDIR}/top.bit: ${BUILDDIR}/vivado.tcl
cd ${BUILDDIR} && vivado -mode batch -source $< > ${LOGFILE} 2>&1
.PHONY: clean
clean:
rm -rf ${BUILDDIR}

View File

@ -1,3 +0,0 @@
set_property INTERNAL_VREF 0.75 [get_iobanks 35]
set_property CONFIG_VOLTAGE 3.3 [current_design]
set_property CFGBVS VCCO [current_design]

View File

@ -1,21 +0,0 @@
#!/usr/bin/env python3
ddr3_name = 'ddr3_top_inst.ddr3_phy_inst'
def get_cells(name_part):
return list(map(lambda c: c.second, filter(lambda c: name_part in c.first, ctx.cells)))
def get_cell(name_part):
return get_cells(name_part)[0]
c1=get_cell(ddr3_name + '.genblk5[0].ISERDESE2_train')
c3=get_cell(ddr3_name + '.genblk5[0].OSERDESE2_train')
# c2=get_cell(ddr3_name + '.genblk5[1].ISERDESE2_train')
# c4=get_cell(ddr3_name + '.genblk5[1].OSERDESE2_train')
# Usually, nextpnr-xilinx would place these on X0Y149, but
# prjxray has missing PIPs on _SING tiles, so we need to
# place these on a non-SING tile to make it work
c1.setAttr('BEL', 'ILOGIC_X1Y193/ISERDESE2')
c3.setAttr('BEL', 'OLOGIC_X1Y193/OSERDESE2')
# c2.setAttr('BEL', 'ILOGIC_X0Y245/ISERDESE2')
# c4.setAttr('BEL', 'OLOGIC_X0Y245/OSERDESE2')

Binary file not shown.

View File

@ -93,34 +93,64 @@
(* mark_debug = "true" *) wire clk_locked;
clk_wiz clk_wiz_inst
(
// Clock out ports
.clk_out1(i_controller_clk), //83.333 Mhz
.clk_out2(i_ddr3_clk), // 333.333 MHz
.clk_out3(i_ref_clk), //200MHz
.clk_out4(i_ddr3_clk_90), // 333.333 MHz with 90degree shift
// Status and control signals
.reset(i_rst),
.locked(clk_locked),
// Clock in ports
.clk_in1(i_clk)
);
// UART module from https://github.com/alexforencich/verilog-uart
uart #(.DATA_WIDTH(8)) uart_m
(
.clk(i_controller_clk),
.rst(i_rst),
.s_axis_tdata(o_wb_data),
.s_axis_tvalid(o_wb_ack),
.s_axis_tready(),
.m_axis_tdata(rd_data),
.m_axis_tvalid(m_axis_tvalid),
.m_axis_tready(1),
.rxd(rx),
.txd(tx),
.prescale(1085) //9600 Baud Rate
// Clock out ports
.clk_out1(i_controller_clk), //83.333 Mhz
.clk_out2(i_ddr3_clk), // 333.333 MHz
.clk_out3(i_ref_clk), //200MHz
.clk_out4(i_ddr3_clk_90), // 333.333 MHz with 90degree shift
// Status and control signals
.reset(i_rst),
.locked(clk_locked),
// Clock in ports
.clk_in1(i_clk)
);
// UART TX/RXmodule from https://github.com/ben-marshall/uart
uart_tx #(
.BIT_RATE(9600),
.CLK_HZ(83_333_333),
.PAYLOAD_BITS(8),
.STOP_BITS(1)
) uart_tx_inst (
.clk(i_controller_clk), // Top level system clock input.
.resetn(!i_rst && clk_locked && o_debug1[4:0] == 23), // Asynchronous active low reset.
.uart_txd(tx), // UART transmit pin.
.uart_tx_busy(), // Module busy sending previous item.
.uart_tx_en(o_wb_ack), // Send the data on uart_tx_data
.uart_tx_data(o_wb_data) // The data to be sent
);
uart_rx #(
.BIT_RATE(9600),
.CLK_HZ(83_333_333),
.PAYLOAD_BITS(8),
.STOP_BITS(1)
) uart_rx_inst (
.clk(i_controller_clk), // Top level system clock input.
.resetn(!i_rst && clk_locked && o_debug1[4:0] == 23), // Asynchronous active low reset.
.uart_rxd(rx), // UART Recieve pin.
.uart_rx_en(o_debug1[4:0] == 23), // Recieve enable
.uart_rx_break(), // Did we get a BREAK message?
.uart_rx_valid(m_axis_tvalid), // Valid data recieved/available.
.uart_rx_data(rd_data) // The recieved data.
);
// UART module from https://github.com/alexforencich/verilog-uart (DOES NOT WORK ON OPENXC7, UberDDR3 cannot finish calibration when this UART is used)
// uart #(.DATA_WIDTH(8)) uart_m
// (
// .clk(i_controller_clk),
// .rst(i_rst),
// .s_axis_tdata(o_wb_data),
// .s_axis_tvalid(o_wb_ack),
// .s_axis_tready(),
// .m_axis_tdata(rd_data),
// .m_axis_tvalid(m_axis_tvalid),
// .m_axis_tready(1),
// .rxd(rx),
// .txd(tx),
// .prescale(1085) //9600 Baud Rate (83.33MHz/(8*9600))
// );
// DDR3 Controller
ddr3_top #(

View File

@ -1,22 +0,0 @@
#!/usr/bin/env python3
ddr3_name = 'ddr3_top_inst.ddr3_phy_inst'
def get_cells(name_part):
return list(map(lambda c: c.second, filter(lambda c: name_part in c.first, ctx.cells)))
def get_cell(name_part):
return get_cells(name_part)[0]
c1_name = ddr3_name + '.genblk5[0].ISERDESE2_train'
# c2_name = ddr3_name + '.genblk5[1].ISERDESE2_train'
c3_name = ddr3_name + '.genblk5[0].OSERDESE2_train'
# c4_name = ddr3_name + '.genblk5[1].OSERDESE2_train'
c1=get_cell(c1_name)
# c2=get_cell(c2_name)
c3=get_cell(c3_name)
# c4=get_cell(c4_name)
print('show_bels: ' + c1_name + ": ", c1.bel)
# print('show_bels: ' + c2_name + ": ", c2.bel)
print('show_bels: ' + c3_name + ": ", c3.bel)
# print('show_bels: ' + c4_name + ": ", c4.bel)

View File

@ -1,113 +0,0 @@
/*
Copyright (c) 2014-2017 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* AXI4-Stream UART
*/
module uart #
(
parameter DATA_WIDTH = 8
)
(
input wire clk,
input wire rst,
/*
* AXI input
*/
input wire [DATA_WIDTH-1:0] s_axis_tdata,
input wire s_axis_tvalid,
output wire s_axis_tready,
/*
* AXI output
*/
output wire [DATA_WIDTH-1:0] m_axis_tdata,
output wire m_axis_tvalid,
input wire m_axis_tready,
/*
* UART interface
*/
input wire rxd,
output wire txd,
/*
* Status
*/
output wire tx_busy,
output wire rx_busy,
output wire rx_overrun_error,
output wire rx_frame_error,
/*
* Configuration
*/
input wire [15:0] prescale
);
uart_tx #(
.DATA_WIDTH(DATA_WIDTH)
)
uart_tx_inst (
.clk(clk),
.rst(rst),
// axi input
.s_axis_tdata(s_axis_tdata),
.s_axis_tvalid(s_axis_tvalid),
.s_axis_tready(s_axis_tready),
// output
.txd(txd),
// status
.busy(tx_busy),
// configuration
.prescale(prescale)
);
uart_rx #(
.DATA_WIDTH(DATA_WIDTH)
)
uart_rx_inst (
.clk(clk),
.rst(rst),
// axi output
.m_axis_tdata(m_axis_tdata),
.m_axis_tvalid(m_axis_tvalid),
.m_axis_tready(m_axis_tready),
// input
.rxd(rxd),
// status
.busy(rx_busy),
.overrun_error(rx_overrun_error),
.frame_error(rx_frame_error),
// configuration
.prescale(prescale)
);
endmodule

View File

@ -1,142 +1,207 @@
/*
Copyright (c) 2014-2017 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* AXI4-Stream UART
*/
module uart_rx #
(
parameter DATA_WIDTH = 8
)
(
input wire clk,
input wire rst,
/*
* AXI output
*/
output wire [DATA_WIDTH-1:0] m_axis_tdata,
output wire m_axis_tvalid,
input wire m_axis_tready,
/*
* UART interface
*/
input wire rxd,
/*
* Status
*/
output wire busy,
output wire overrun_error,
output wire frame_error,
/*
* Configuration
*/
input wire [15:0] prescale
//
// Module: uart_rx
//
// Notes:
// - UART reciever module.
//
module uart_rx(
input wire clk , // Top level system clock input.
input wire resetn , // Asynchronous active low reset.
input wire uart_rxd , // UART Recieve pin.
input wire uart_rx_en , // Recieve enable
output wire uart_rx_break, // Did we get a BREAK message?
output wire uart_rx_valid, // Valid data recieved and available.
output reg [PAYLOAD_BITS-1:0] uart_rx_data // The recieved data.
);
reg [DATA_WIDTH-1:0] m_axis_tdata_reg = 0;
reg m_axis_tvalid_reg = 0;
// ---------------------------------------------------------------------------
// External parameters.
//
reg rxd_reg = 1;
//
// Input bit rate of the UART line.
parameter BIT_RATE = 9600; // bits / sec
localparam BIT_P = 1_000_000_000 * 1/BIT_RATE; // nanoseconds
reg busy_reg = 0;
reg overrun_error_reg = 0;
reg frame_error_reg = 0;
//
// Clock frequency in hertz.
parameter CLK_HZ = 50_000_000;
localparam CLK_P = 1_000_000_000 * 1/CLK_HZ; // nanoseconds
reg [DATA_WIDTH-1:0] data_reg = 0;
reg [18:0] prescale_reg = 0;
reg [3:0] bit_cnt = 0;
//
// Number of data bits recieved per UART packet.
parameter PAYLOAD_BITS = 8;
assign m_axis_tdata = m_axis_tdata_reg;
assign m_axis_tvalid = m_axis_tvalid_reg;
//
// Number of stop bits indicating the end of a packet.
parameter STOP_BITS = 1;
assign busy = busy_reg;
assign overrun_error = overrun_error_reg;
assign frame_error = frame_error_reg;
// --------------------------------------------------------------------------
// Internal parameters.
//
//
// Number of clock cycles per uart bit.
localparam CYCLES_PER_BIT = BIT_P / CLK_P;
//
// Size of the registers which store sample counts and bit durations.
localparam COUNT_REG_LEN = 1+$clog2(CYCLES_PER_BIT);
// --------------------------------------------------------------------------
// Internal registers.
//
//
// Internally latched value of the uart_rxd line. Helps break long timing
// paths from input pins into the logic.
reg rxd_reg;
reg rxd_reg_0;
//
// Storage for the recieved serial data.
reg [PAYLOAD_BITS-1:0] recieved_data;
//
// Counter for the number of cycles over a packet bit.
reg [COUNT_REG_LEN-1:0] cycle_counter;
//
// Counter for the number of recieved bits of the packet.
reg [3:0] bit_counter;
//
// Sample of the UART input line whenever we are in the middle of a bit frame.
reg bit_sample;
//
// Current and next states of the internal FSM.
reg [2:0] fsm_state;
reg [2:0] n_fsm_state;
localparam FSM_IDLE = 0;
localparam FSM_START= 1;
localparam FSM_RECV = 2;
localparam FSM_STOP = 3;
// ---------------------------------------------------------------------------
// Output assignment
//
assign uart_rx_break = uart_rx_valid && ~|recieved_data;
assign uart_rx_valid = fsm_state == FSM_STOP && n_fsm_state == FSM_IDLE;
always @(posedge clk) begin
if (rst) begin
m_axis_tdata_reg <= 0;
m_axis_tvalid_reg <= 0;
rxd_reg <= 1;
prescale_reg <= 0;
bit_cnt <= 0;
busy_reg <= 0;
overrun_error_reg <= 0;
frame_error_reg <= 0;
end else begin
rxd_reg <= rxd;
overrun_error_reg <= 0;
frame_error_reg <= 0;
if(!resetn) begin
uart_rx_data <= {PAYLOAD_BITS{1'b0}};
end else if (fsm_state == FSM_STOP) begin
uart_rx_data <= recieved_data;
end
end
if (m_axis_tvalid && m_axis_tready) begin
m_axis_tvalid_reg <= 0;
end
// ---------------------------------------------------------------------------
// FSM next state selection.
//
if (prescale_reg > 0) begin
prescale_reg <= prescale_reg - 1;
end else if (bit_cnt > 0) begin
if (bit_cnt > DATA_WIDTH+1) begin
if (!rxd_reg) begin
bit_cnt <= bit_cnt - 1;
prescale_reg <= (prescale << 3)-1;
end else begin
bit_cnt <= 0;
prescale_reg <= 0;
end
end else if (bit_cnt > 1) begin
bit_cnt <= bit_cnt - 1;
prescale_reg <= (prescale << 3)-1;
data_reg <= {rxd_reg, data_reg[DATA_WIDTH-1:1]};
end else if (bit_cnt == 1) begin
bit_cnt <= bit_cnt - 1;
if (rxd_reg) begin
m_axis_tdata_reg <= data_reg;
m_axis_tvalid_reg <= 1;
overrun_error_reg <= m_axis_tvalid_reg;
end else begin
frame_error_reg <= 1;
end
end
end else begin
busy_reg <= 0;
if (!rxd_reg) begin
prescale_reg <= (prescale << 2)-2;
bit_cnt <= DATA_WIDTH+2;
data_reg <= 0;
busy_reg <= 1;
end
wire next_bit = cycle_counter == CYCLES_PER_BIT ||
fsm_state == FSM_STOP &&
cycle_counter == CYCLES_PER_BIT/2;
wire payload_done = bit_counter == PAYLOAD_BITS ;
//
// Handle picking the next state.
always @(*) begin : p_n_fsm_state
case(fsm_state)
FSM_IDLE : n_fsm_state = rxd_reg ? FSM_IDLE : FSM_START;
FSM_START: n_fsm_state = next_bit ? FSM_RECV : FSM_START;
FSM_RECV : n_fsm_state = payload_done ? FSM_STOP : FSM_RECV ;
FSM_STOP : n_fsm_state = next_bit ? FSM_IDLE : FSM_STOP ;
default : n_fsm_state = FSM_IDLE;
endcase
end
// ---------------------------------------------------------------------------
// Internal register setting and re-setting.
//
//
// Handle updates to the recieved data register.
integer i = 0;
always @(posedge clk) begin : p_recieved_data
if(!resetn) begin
recieved_data <= {PAYLOAD_BITS{1'b0}};
end else if(fsm_state == FSM_IDLE ) begin
recieved_data <= {PAYLOAD_BITS{1'b0}};
end else if(fsm_state == FSM_RECV && next_bit ) begin
recieved_data[PAYLOAD_BITS-1] <= bit_sample;
for ( i = PAYLOAD_BITS-2; i >= 0; i = i - 1) begin
recieved_data[i] <= recieved_data[i+1];
end
end
end
//
// Increments the bit counter when recieving.
always @(posedge clk) begin : p_bit_counter
if(!resetn) begin
bit_counter <= 4'b0;
end else if(fsm_state != FSM_RECV) begin
bit_counter <= {COUNT_REG_LEN{1'b0}};
end else if(fsm_state == FSM_RECV && next_bit) begin
bit_counter <= bit_counter + 1'b1;
end
end
//
// Sample the recieved bit when in the middle of a bit frame.
always @(posedge clk) begin : p_bit_sample
if(!resetn) begin
bit_sample <= 1'b0;
end else if (cycle_counter == CYCLES_PER_BIT/2) begin
bit_sample <= rxd_reg;
end
end
//
// Increments the cycle counter when recieving.
always @(posedge clk) begin : p_cycle_counter
if(!resetn) begin
cycle_counter <= {COUNT_REG_LEN{1'b0}};
end else if(next_bit) begin
cycle_counter <= {COUNT_REG_LEN{1'b0}};
end else if(fsm_state == FSM_START ||
fsm_state == FSM_RECV ||
fsm_state == FSM_STOP ) begin
cycle_counter <= cycle_counter + 1'b1;
end
end
//
// Progresses the next FSM state.
always @(posedge clk) begin : p_fsm_state
if(!resetn) begin
fsm_state <= FSM_IDLE;
end else begin
fsm_state <= n_fsm_state;
end
end
//
// Responsible for updating the internal value of the rxd_reg.
always @(posedge clk) begin : p_rxd_reg
if(!resetn) begin
rxd_reg <= 1'b1;
rxd_reg_0 <= 1'b1;
end else if(uart_rx_en) begin
rxd_reg <= rxd_reg_0;
rxd_reg_0 <= uart_rxd;
end
end
endmodule

View File

@ -1,115 +1,187 @@
/*
Copyright (c) 2014-2017 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
//
// Module: uart_tx
//
// Notes:
// - UART transmitter module.
//
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* AXI4-Stream UART
*/
module uart_tx #
(
parameter DATA_WIDTH = 8
)
(
input wire clk,
input wire rst,
/*
* AXI input
*/
input wire [DATA_WIDTH-1:0] s_axis_tdata,
input wire s_axis_tvalid,
output wire s_axis_tready,
/*
* UART interface
*/
output wire txd,
/*
* Status
*/
output wire busy,
/*
* Configuration
*/
input wire [15:0] prescale
module uart_tx(
input wire clk , // Top level system clock input.
input wire resetn , // Asynchronous active low reset.
output wire uart_txd , // UART transmit pin.
output wire uart_tx_busy, // Module busy sending previous item.
input wire uart_tx_en , // Send the data on uart_tx_data
input wire [PAYLOAD_BITS-1:0] uart_tx_data // The data to be sent
);
reg s_axis_tready_reg = 0;
// ---------------------------------------------------------------------------
// External parameters.
//
reg txd_reg = 1;
//
// Input bit rate of the UART line.
parameter BIT_RATE = 9600; // bits / sec
localparam BIT_P = 1_000_000_000 * 1/BIT_RATE; // nanoseconds
reg busy_reg = 0;
//
// Clock frequency in hertz.
parameter CLK_HZ = 50_000_000;
localparam CLK_P = 1_000_000_000 * 1/CLK_HZ; // nanoseconds
reg [DATA_WIDTH:0] data_reg = 0;
reg [18:0] prescale_reg = 0;
reg [3:0] bit_cnt = 0;
//
// Number of data bits recieved per UART packet.
parameter PAYLOAD_BITS = 8;
assign s_axis_tready = s_axis_tready_reg;
assign txd = txd_reg;
//
// Number of stop bits indicating the end of a packet.
parameter STOP_BITS = 1;
assign busy = busy_reg;
// ---------------------------------------------------------------------------
// Internal parameters.
//
always @(posedge clk) begin
if (rst) begin
s_axis_tready_reg <= 0;
txd_reg <= 1;
prescale_reg <= 0;
bit_cnt <= 0;
busy_reg <= 0;
end else begin
if (prescale_reg > 0) begin
s_axis_tready_reg <= 0;
prescale_reg <= prescale_reg - 1;
end else if (bit_cnt == 0) begin
s_axis_tready_reg <= 1;
busy_reg <= 0;
//
// Number of clock cycles per uart bit.
localparam CYCLES_PER_BIT = BIT_P / CLK_P;
if (s_axis_tvalid) begin
s_axis_tready_reg <= !s_axis_tready_reg;
prescale_reg <= (prescale << 3)-1;
bit_cnt <= DATA_WIDTH+1;
data_reg <= {1'b1, s_axis_tdata};
txd_reg <= 0;
busy_reg <= 1;
end
end else begin
if (bit_cnt > 1) begin
bit_cnt <= bit_cnt - 1;
prescale_reg <= (prescale << 3)-1;
{data_reg, txd_reg} <= {1'b0, data_reg};
end else if (bit_cnt == 1) begin
bit_cnt <= bit_cnt - 1;
prescale_reg <= (prescale << 3);
txd_reg <= 1;
end
//
// Size of the registers which store sample counts and bit durations.
localparam COUNT_REG_LEN = 1+$clog2(CYCLES_PER_BIT);
// ---------------------------------------------------------------------------
// Internal registers.
//
//
// Internally latched value of the uart_txd line. Helps break long timing
// paths from the logic to the output pins.
reg txd_reg;
//
// Storage for the serial data to be sent.
reg [PAYLOAD_BITS-1:0] data_to_send;
//
// Counter for the number of cycles over a packet bit.
reg [COUNT_REG_LEN-1:0] cycle_counter;
//
// Counter for the number of sent bits of the packet.
reg [3:0] bit_counter;
//
// Current and next states of the internal FSM.
reg [2:0] fsm_state;
reg [2:0] n_fsm_state;
localparam FSM_IDLE = 0;
localparam FSM_START= 1;
localparam FSM_SEND = 2;
localparam FSM_STOP = 3;
// ---------------------------------------------------------------------------
// FSM next state selection.
//
assign uart_tx_busy = fsm_state != FSM_IDLE;
assign uart_txd = txd_reg;
wire next_bit = cycle_counter == CYCLES_PER_BIT;
wire payload_done = bit_counter == PAYLOAD_BITS ;
wire stop_done = bit_counter == STOP_BITS && fsm_state == FSM_STOP;
//
// Handle picking the next state.
always @(*) begin : p_n_fsm_state
case(fsm_state)
FSM_IDLE : n_fsm_state = uart_tx_en ? FSM_START: FSM_IDLE ;
FSM_START: n_fsm_state = next_bit ? FSM_SEND : FSM_START;
FSM_SEND : n_fsm_state = payload_done ? FSM_STOP : FSM_SEND ;
FSM_STOP : n_fsm_state = stop_done ? FSM_IDLE : FSM_STOP ;
default : n_fsm_state = FSM_IDLE;
endcase
end
// ---------------------------------------------------------------------------
// Internal register setting and re-setting.
//
//
// Handle updates to the sent data register.
integer i = 0;
always @(posedge clk) begin : p_data_to_send
if(!resetn) begin
data_to_send <= {PAYLOAD_BITS{1'b0}};
end else if(fsm_state == FSM_IDLE && uart_tx_en) begin
data_to_send <= uart_tx_data;
end else if(fsm_state == FSM_SEND && next_bit ) begin
for ( i = PAYLOAD_BITS-2; i >= 0; i = i - 1) begin
data_to_send[i] <= data_to_send[i+1];
end
end
end
//
// Increments the bit counter each time a new bit frame is sent.
always @(posedge clk) begin : p_bit_counter
if(!resetn) begin
bit_counter <= 4'b0;
end else if(fsm_state != FSM_SEND && fsm_state != FSM_STOP) begin
bit_counter <= {COUNT_REG_LEN{1'b0}};
end else if(fsm_state == FSM_SEND && n_fsm_state == FSM_STOP) begin
bit_counter <= {COUNT_REG_LEN{1'b0}};
end else if(fsm_state == FSM_STOP&& next_bit) begin
bit_counter <= bit_counter + 1'b1;
end else if(fsm_state == FSM_SEND && next_bit) begin
bit_counter <= bit_counter + 1'b1;
end
end
//
// Increments the cycle counter when sending.
always @(posedge clk) begin : p_cycle_counter
if(!resetn) begin
cycle_counter <= {COUNT_REG_LEN{1'b0}};
end else if(next_bit) begin
cycle_counter <= {COUNT_REG_LEN{1'b0}};
end else if(fsm_state == FSM_START ||
fsm_state == FSM_SEND ||
fsm_state == FSM_STOP ) begin
cycle_counter <= cycle_counter + 1'b1;
end
end
//
// Progresses the next FSM state.
always @(posedge clk) begin : p_fsm_state
if(!resetn) begin
fsm_state <= FSM_IDLE;
end else begin
fsm_state <= n_fsm_state;
end
end
//
// Responsible for updating the internal value of the txd_reg.
always @(posedge clk) begin : p_txd_reg
if(!resetn) begin
txd_reg <= 1'b1;
end else if(fsm_state == FSM_IDLE) begin
txd_reg <= 1'b1;
end else if(fsm_state == FSM_START) begin
txd_reg <= 1'b0;
end else if(fsm_state == FSM_SEND) begin
txd_reg <= data_to_send[0];
end else if(fsm_state == FSM_STOP) begin
txd_reg <= 1'b1;
end
end
endmodule