diff --git a/uart/Makefile b/uart/Makefile new file mode 100644 index 0000000..2622d4e --- /dev/null +++ b/uart/Makefile @@ -0,0 +1,56 @@ +# Project setup +TOP ?= top +SIM = iverilog +WAVE = vvp + +PCF = constraints/iceFUN.pcf +TIMING = constraints/timing.py +YOSYS = yosys +PNR = nextpnr-ice40 +IPACK = icepack +BURN = iceFUNprog +SBY = sby + +BUILD_DIR = ./build +VCD = $(BUILD_DIR)/waveform.vcd +BIN_TARGET = build/top.bin + +# Files +MODULES += $(wildcard rtl/*.v) +TEST = tb.v + +define colorecho + @tput setaf 6 + @echo $1 + @tput sgr0 +endef + +.PHONY: all clean burn + +all: sim + +sim: $(TEST) $(MODULES) + @mkdir -p $(BUILD_DIR) + @$(SIM) -o $(BUILD_DIR)/tb_out $< $(MODULES) && $(WAVE) $(BUILD_DIR)/tb_out && open $(VCD) + +$(BUILD_DIR)/top.json: $(MODULES) + $(call colorecho, "Synthesizing ...") + mkdir -p $(BUILD_DIR) + $(YOSYS) -p "synth_ice40 -top uart_echo -json build/top.json" -q $^ + +$(BIN_TARGET): $(BUILD_DIR)/top.json $(PCF) $(TIMING) + $(call colorecho, "Routing and building binary stream ...") + $(PNR) -r --hx8k --json $< --package cb132 \ + --asc $(BUILD_DIR)/top.asc --opt-timing --pcf $(PCF) \ + --pre-pack $(TIMING) -l $(BUILD_DIR)/pnr_report.txt -q + $(IPACK) $(BUILD_DIR)/top.asc $@ + $(call colorecho, "Done!") + +burn: $(BIN_TARGET) + $(BURN) $< + +fv: + $(SBY) -f $(FV_SRC) -d $(BUILD_DIR)/fv + +clean: + rm -rf $(BUILD_DIR) diff --git a/uart/bench/uart_rx_tb.v b/uart/bench/uart_rx_tb.v new file mode 100644 index 0000000..b4bade3 --- /dev/null +++ b/uart/bench/uart_rx_tb.v @@ -0,0 +1,78 @@ +`timescale 1ns/1ps +`define IVERILOG 1 +`default_nettype none + +module uart_rx_tb; +/*autowire*/ +// Beginning of automatic wires (for undeclared instantiated-module outputs) +wire [7:0] data_o; // From uut of uart_rx.v +wire rx_done_o; // From uut of uart_rx.v +// End of automatics +/*autoreginput*/ +// Beginning of automatic reg inputs (for undeclared instantiated-module inputs) +reg clk; // To uut of uart_rx.v +reg rst_n; // To uut of uart_rx.v +reg rx_i; // To uut of uart_rx.v +// End of automatics + +localparam clk_period = 20; +localparam clocks_per_baud = 20; +uart_rx #(/*autoinstparam*/ + // Parameters + .CLOCKS_PER_BAUD (clocks_per_baud - 1)) + uut (/*autoinst*/ + // Outputs + .data_o (data_o[7:0]), + .rx_done_o (rx_done_o), + // Inputs + .clk (clk), + .rst_n (rst_n), + .rx_i (rx_i)); + +initial begin + $dumpfile("build/waveform.vcd"); + $dumpvars(0, uut); + + clk = 1'b1; + rst_n = 1'b1; + rx_i = 1'b1; +end + +always + #(clk_period/2) clk = ~clk; + +initial begin + #clk_period; + rst_n = 0; // start reset + #clk_period; + rst_n = 1; // finish reset + #(clk_period * 50); + + rx_i = 0; // start bit + #(clk_period * clocks_per_baud); + rx_i = 1; // bit 0 + #(clk_period * clocks_per_baud); + rx_i = 0; // bit 1 + #(clk_period * clocks_per_baud); + rx_i = 1; // bit 2 + #(clk_period * clocks_per_baud); + rx_i = 0; // bit 3 + #(clk_period * clocks_per_baud); + rx_i = 1; // bit 4 + #(clk_period * clocks_per_baud); + rx_i = 0; // bit 5 + #(clk_period * clocks_per_baud); + rx_i = 1; // bit 6 + #(clk_period * clocks_per_baud); + rx_i = 0; // bit 7 + #(clk_period * clocks_per_baud); + rx_i = 1; // stop bit + #(clk_period * clocks_per_baud); + + #800 $finish; // finish at 200 ticks +end +endmodule // end of uart_rx_tb + + // Local Variables: + // verilog-library-directories:(".." "../rtl" ".") + // End: diff --git a/uart/bench/uart_top_tb.v b/uart/bench/uart_top_tb.v new file mode 100644 index 0000000..ac2a154 --- /dev/null +++ b/uart/bench/uart_top_tb.v @@ -0,0 +1,69 @@ +`timescale 1ns/100ps +`define IVERILOG 1 + +module uart_top_tb; +/*autowire*/ +// Beginning of automatic wires (for undeclared instantiated-module outputs) +wire tx_o; // From uut of uart_top.v +// End of automatics +/*autoreginput*/ +// Beginning of automatic reg inputs (for undeclared instantiated-module inputs) +reg clk; // To uut of uart_top.v +reg rst_n; // To uut of uart_top.v +reg rx_i; // To uut of uart_top.v +// End of automatics + +localparam T = 10; // clock cycle is 10 ticks +localparam clocks_per_baud = 20; +uart_echo #(/*autoinstparam*/ + // Parameters + .CLOCKS_PER_BAUD (clocks_per_baud-1)) + uut (/*autoinst*/ + // Outputs + .tx_o (tx_o), + // Inputs + .clk (clk), + .rst_n (rst_n), + .rx_i (rx_i)); + +// setup dump and reset +initial begin + $dumpfile("build/waveform.vcd"); + $dumpvars(0, uut); + + clk = 1'b1; + rst_n = 1'b1; +end + +// clocking +always #(T/2) clk = ~clk; + +// when to finish +initial #4000 $finish; // finish at 200 ticks + +// other stimulus +initial begin + rx_i = 1; + #(T) rst_n = 0; + #(T) rst_n = 1; + #(10*T); + + + rx_i = 0; // start bit + #(T * clocks_per_baud) rx_i = 1; // bit 0 + #(T * clocks_per_baud) rx_i = 0; // bit 1 + #(T * clocks_per_baud) rx_i = 1; // bit 2 + #(T * clocks_per_baud) rx_i = 0; // bit 3 + #(T * clocks_per_baud) rx_i = 1; // bit 4 + #(T * clocks_per_baud) rx_i = 0; // bit 5 + #(T * clocks_per_baud) rx_i = 1; // bit 6 + #(T * clocks_per_baud) rx_i = 0; // bit 7 + #(T * clocks_per_baud) rx_i = 1; // stop bit + #(T * clocks_per_baud); + +end +endmodule // end of uart_top_tb + + // Local Variables: + // verilog-library-directories:(".." "../rtl" ".") + // End: diff --git a/uart/bench/uart_tx_tb.v b/uart/bench/uart_tx_tb.v new file mode 100644 index 0000000..cc631e5 --- /dev/null +++ b/uart/bench/uart_tx_tb.v @@ -0,0 +1,63 @@ +`timescale 1ns/1ps +`define IVERILOG 1 + +module uart_tx_tb; +/*autowire*/ +// Beginning of automatic wires (for undeclared instantiated-module outputs) +wire tx_done_o; // From uut of uart_tx.v +wire tx_o; // From uut of uart_tx.v +// End of automatics +/*autoreginput*/ +// Beginning of automatic reg inputs (for undeclared instantiated-module inputs) +reg clk; // To uut of uart_tx.v +reg [7:0] data_i; // To uut of uart_tx.v +reg en_i; // To uut of uart_tx.v +reg rst_n; // To uut of uart_tx.v +// End of automatics +localparam CLK_PERIOD = 10; +uart_tx #(/*autoinstparam*/ + // Parameters + .CLOCKS_PER_BAUD (CLK_PERIOD - 1)) uut (/*autoinst*/ + // Outputs + .tx_o (tx_o), + .tx_done_o (tx_done_o), + // Inputs + .clk (clk), + .rst_n (rst_n), + .en_i (en_i), + .data_i (data_i[7:0])); + +initial begin + $dumpfile("build/waveform.vcd"); + $dumpvars(0, uut); + + clk = 1'b1; + rst_n = 1'b1; + data_i = 0; +end + +always + #(CLK_PERIOD/2) clk = ~clk; + +initial begin + #(CLK_PERIOD); + + rst_n = 0; + #(CLK_PERIOD); + rst_n = 1; + #(CLK_PERIOD); + + data_i = 8'h55; + en_i = 1; + #(CLK_PERIOD); + + en_i = 0; + #(200 * CLK_PERIOD); + + #400 $finish; // finish at 200 ticks +end +endmodule // end of uart_tx_tb + + // Local Variables: + // verilog-library-directories:(".." "../rtl" ".") + // End: diff --git a/uart/constraints/iceFUN.pcf b/uart/constraints/iceFUN.pcf new file mode 100644 index 0000000..cf8f07d --- /dev/null +++ b/uart/constraints/iceFUN.pcf @@ -0,0 +1,8 @@ +# For iceFUN board + +set_io --warn-no-port clk P7 +# set_io --warn-no-port i_start_tx C11 +set_io --warn-no-port rst_n C6 + +set_io --warn-no-port tx_o A3 +set_io --warn-no-port rx_i A1 diff --git a/uart/constraints/timing.py b/uart/constraints/timing.py new file mode 100644 index 0000000..b73bdf6 --- /dev/null +++ b/uart/constraints/timing.py @@ -0,0 +1 @@ +ctx.addClock("clk", 12) diff --git a/uart/rtl/uart_echo.v b/uart/rtl/uart_echo.v new file mode 100644 index 0000000..396c8e8 --- /dev/null +++ b/uart/rtl/uart_echo.v @@ -0,0 +1,37 @@ +`default_nettype none +module uart_echo #(parameter CLOCKS_PER_BAUD=16'd104)( + input wire clk, + input wire rst_n, + input wire rx_i, + output wire tx_o + ); + +wire tx_en; +wire [7:0] tx_data; + +uart_rx #(/*autoinstparam*/ + // Parameters + .CLOCKS_PER_BAUD (CLOCKS_PER_BAUD)) rx (/*autoinst*/ + // Outputs + .data_o (tx_data), + .rx_done_o (tx_en), + // Inputs + .clk (clk), + .rst_n (rst_n), + .rx_i (rx_i)); +uart_tx #(/*autoinstparam*/ + // Parameters + .CLOCKS_PER_BAUD (CLOCKS_PER_BAUD)) tx (/*autoinst*/ + // Outputs + .tx_o (tx_o), + .tx_done_o (), + // Inputs + .clk (clk), + .rst_n (rst_n), + .en_i (tx_en), + .data_i (tx_data)); + +endmodule + // Local Variables: + // verilog-library-directories:(".." "../rtl" ".") + // End: diff --git a/uart/rtl/uart_rx.v b/uart/rtl/uart_rx.v new file mode 100644 index 0000000..15d09f7 --- /dev/null +++ b/uart/rtl/uart_rx.v @@ -0,0 +1,116 @@ +`default_nettype none +module uart_rx #(parameter CLOCKS_PER_BAUD=16'd868)( + input clk, + input rst_n, + input rx_i, + output reg [7:0] data_o, + output reg rx_done_o + ); + +localparam clocks_per_half_bit = CLOCKS_PER_BAUD / 2; + +localparam s_idle = 5'b00001, + s_start = 5'b00010, + s_rd = 5'b00100, + s_stop = 5'b01000, + s_done = 5'b10000; + +reg en_cnt; +reg [15:0] cnt; +reg [4:0] state; +reg [2:0] rx_bits; + +always @(posedge clk or negedge rst_n) begin + if (!rst_n) + cnt <= 16'd0; + else if ((en_cnt == 0) || (cnt == CLOCKS_PER_BAUD)) + cnt <= 16'd0; + else + cnt <= cnt + 1; +end + +// edge detection +reg rx_0, rx_1, rx_2, rx_3; +always @(posedge clk or negedge rst_n) begin + if (!rst_n) begin + rx_0 <= 0; + rx_1 <= 0; + rx_2 <= 0; + rx_3 <= 0; + end else begin + rx_3 <= rx_i; + rx_2 <= rx_3; + rx_1 <= rx_2; + rx_0 <= rx_1; + end +end + +wire start_flag; +assign start_flag = rx_0 & rx_1 & (~rx_2) &(~rx_3); + +always @(posedge clk or negedge rst_n) begin + if (~rst_n) begin + state <= s_idle; + en_cnt <= 0; + data_o <= 0; + rx_bits <= 0; + rx_done_o <= 0; + end else begin + case (state) + s_idle: begin + rx_bits <= 0; + rx_done_o <= 0; + if (start_flag) begin + en_cnt <= 1; + state <= s_start; + end else begin + en_cnt <= 0; + state <= s_idle; + end + end + + s_start: begin + if (cnt == clocks_per_half_bit) + if (rx_i == 0) + state <= s_rd; + else + state <= s_idle; + end + + s_rd: begin + if (cnt == clocks_per_half_bit) + if (rx_bits == 3'd7) + state <= s_stop; + else begin + data_o[rx_bits] <= rx_i; + rx_bits <= rx_bits + 1; + state <= s_rd; + end + end + + s_stop: begin + if (cnt == clocks_per_half_bit) + if (rx_i == 1) + state <= s_done; + else + state <= s_idle; + end + + s_done: begin + en_cnt <= 0; + rx_done_o <= 1; + state <= s_idle; + end + + default : begin + state <= s_idle; + end + endcase + end +end + +endmodule + + // Local Variables: + // verilog-library-directories:(".." "../rtl" ".") + // End: diff --git a/uart/rtl/uart_tx.v b/uart/rtl/uart_tx.v new file mode 100644 index 0000000..c45e6a8 --- /dev/null +++ b/uart/rtl/uart_tx.v @@ -0,0 +1,103 @@ +`default_nettype none +module uart_tx #(parameter CLOCKS_PER_BAUD=16'd868)( + input wire clk, + input wire rst_n, + input wire en_i, + input wire [7:0] data_i, + output reg tx_o, + output reg tx_done_o + ); + +localparam s_idle = 5'b00001, + s_start = 5'b00010, + s_wr = 5'b00100, + s_stop = 5'b01000, + s_done = 5'b10000; + +reg en_cnt; +reg [15:0] cnt; +reg [4:0] state; +reg [7:0] data_r; +reg [2:0] tx_bits; + +always @(posedge clk or negedge rst_n) begin + if (!rst_n) + cnt <= 16'd0; + else if ((en_cnt == 0) || (cnt == CLOCKS_PER_BAUD)) + cnt <= 16'd0; + else + cnt <= cnt + 1; +end + +always @(posedge clk or negedge rst_n) begin + if (~rst_n) begin + state <= s_idle; + tx_o <= 1; + en_cnt <= 0; + data_r <= 0; + tx_bits <= 0; + tx_done_o <= 0; + end else begin + case (state) + s_idle: begin + data_r <= data_i; + tx_bits <= 0; + tx_done_o <= 0; + if (en_i == 1) begin + en_cnt <= 1; + state <= s_start; + end else begin + en_cnt <= 0; + state <= s_idle; + end + end + + s_start: begin + if (cnt == CLOCKS_PER_BAUD) + state <= s_wr; + else begin + tx_o <= 0; + state <= s_start; + end + end + + s_wr: begin + if (cnt == CLOCKS_PER_BAUD) begin + if (tx_bits == 3'd7) + state <= s_stop; + else begin + tx_bits <= tx_bits + 1; + state <= s_wr; + end + end else begin + tx_o <= data_r[tx_bits]; + state <= s_wr; + end + end + + s_stop: begin + if (cnt == CLOCKS_PER_BAUD) + state <= s_done; + else begin + tx_o <= 1; + state <= s_stop; + end + end + + s_done: begin + en_cnt <= 0; + tx_done_o <= 1; + state <= s_idle; + end + + default : begin + state <= s_idle; + end + endcase + end +end + +endmodule + // Local Variables: + // verilog-library-directories:(".." "../rtl" ".") + // End: diff --git a/uart/tb.v b/uart/tb.v new file mode 120000 index 0000000..499cd2f --- /dev/null +++ b/uart/tb.v @@ -0,0 +1 @@ +bench/uart_top_tb.v \ No newline at end of file