MERGE: SRAM to UART
diff --git a/Makefile b/Makefile index d2ba127..381430b 100644 --- a/Makefile +++ b/Makefile
@@ -79,7 +79,11 @@ docker_run_verify=\ docker run -v ${TARGET_PATH}:${TARGET_PATH} -v ${PDK_ROOT}:${PDK_ROOT} \ -v ${CARAVEL_ROOT}:${CARAVEL_ROOT} \ +<<<<<<< HEAD + -v ${MCW_ROOT}:${MCW_ROOT} \ +======= -v $(MCW_ROOT):$(MCW_ROOT) \ +>>>>>>> sram -e TARGET_PATH=${TARGET_PATH} -e PDK_ROOT=${PDK_ROOT} \ -e CARAVEL_ROOT=${CARAVEL_ROOT} \ -e TOOLS=/opt/riscv32i \ @@ -87,6 +91,7 @@ -e CORE_VERILOG_PATH=$(MCW_ROOT)/verilog \ -e GCC_PREFIX=riscv32-unknown-elf \ -e MCW_ROOT=$(MCW_ROOT) \ + -e CARAVEL_PATH=$(CARAVEL_ROOT) \ -u $$(id -u $$USER):$$(id -g $$USER) efabless/dv_setup:latest \ sh -c $(verify_command)
diff --git a/verilog/dv/wb_port/Makefile b/verilog/dv/wb_port/Makefile index 3fd0b56..b07bb75 100644 --- a/verilog/dv/wb_port/Makefile +++ b/verilog/dv/wb_port/Makefile
@@ -20,7 +20,8 @@ BLOCKS := $(shell basename $(PWDD)) # ---- Include Partitioned Makefiles ---- - +CARAVEL_PATH = $(CARAVEL_ROOT) +CARAVEL_VERILOG_PATH = $(CARAVEL_ROOT)/verilog CONFIG = caravel_user_project @@ -28,5 +29,3 @@ include $(MCW_ROOT)/verilog/dv/make/var.makefile include $(MCW_ROOT)/verilog/dv/make/cpu.makefile include $(MCW_ROOT)/verilog/dv/make/sim.makefile - -
diff --git a/verilog/dv/wb_port/wb_port.c b/verilog/dv/wb_port/wb_port.c index e575fcd..b5d1fa0 100644 --- a/verilog/dv/wb_port/wb_port.c +++ b/verilog/dv/wb_port/wb_port.c
@@ -19,45 +19,47 @@ #include <defs.h> #include <stub.c> -// User Project Slaves (0x3000_0000) -#define sram_offset (*(volatile uint32_t*)0x30000000) - /* - Wishbone Test: - - Configures MPRJ lower 8-IO pins as outputs - - Checks counter value through the wishbone port + Wishbone Test: + - Configures MPRJ lower 8-IO pins as outputs + - Checks counter value through the wishbone port */ +#define reg_UART_SETUP (*(volatile uint32_t*)0x30001000) +#define reg_UART_FIFO (*(volatile uint32_t*)0x30001004) +#define reg_UART_RX_DATA (*(volatile uint32_t*)0x30001008) +#define reg_UART_TX_DATA (*(volatile uint32_t*)0x3000100C) + + + void main() { - /* - IO Control Registers - | DM | VTRIP | SLOW | AN_POL | AN_SEL | AN_EN | MOD_SEL | INP_DIS | HOLDH | OEB_N | MGMT_EN | - | 3-bits | 1-bit | 1-bit | 1-bit | 1-bit | 1-bit | 1-bit | 1-bit | 1-bit | 1-bit | 1-bit | - Output: 0000_0110_0000_1110 (0x1808) = GPIO_MODE_USER_STD_OUTPUT - | DM | VTRIP | SLOW | AN_POL | AN_SEL | AN_EN | MOD_SEL | INP_DIS | HOLDH | OEB_N | MGMT_EN | - | 110 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | + /* + IO Control Registers + | DM | VTRIP | SLOW | AN_POL | AN_SEL | AN_EN | MOD_SEL | INP_DIS | HOLDH | OEB_N | MGMT_EN | + | 3-bits | 1-bit | 1-bit | 1-bit | 1-bit | 1-bit | 1-bit | 1-bit | 1-bit | 1-bit | 1-bit | + Output: 0000_0110_0000_1110 (0x1808) = GPIO_MODE_USER_STD_OUTPUT + | DM | VTRIP | SLOW | AN_POL | AN_SEL | AN_EN | MOD_SEL | INP_DIS | HOLDH | OEB_N | MGMT_EN | + | 110 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | + + + Input: 0000_0001_0000_1111 (0x0402) = GPIO_MODE_USER_STD_INPUT_NOPULL + | DM | VTRIP | SLOW | AN_POL | AN_SEL | AN_EN | MOD_SEL | INP_DIS | HOLDH | OEB_N | MGMT_EN | + | 001 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | + */ + /* Set up the housekeeping SPI to be connected internally so */ + /* that external pin changes don't affect it. */ - Input: 0000_0001_0000_1111 (0x0402) = GPIO_MODE_USER_STD_INPUT_NOPULL - | DM | VTRIP | SLOW | AN_POL | AN_SEL | AN_EN | MOD_SEL | INP_DIS | HOLDH | OEB_N | MGMT_EN | - | 001 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | - */ - - int number_of_test = 5; - int fail = 0; - - /* Set up the housekeeping SPI to be connected internally so */ - /* that external pin changes don't affect it. */ reg_spi_enable = 1; reg_wb_enable = 1; - // reg_spimaster_config = 0xa002; // Enable, prescaler = 2, + // reg_spimaster_config = 0xa002; // Enable, prescaler = 2, // connect to housekeeping SPI - // Connect the housekeeping SPI to the SPI master - // so that the CSB line is not left floating. This allows - // all of the GPIO pins to be used for user functions. + // Connect the housekeeping SPI to the SPI master + // so that the CSB line is not left floating. This allows + // all of the GPIO pins to be used for user functions. reg_mprj_io_31 = GPIO_MODE_MGMT_STD_OUTPUT; reg_mprj_io_30 = GPIO_MODE_MGMT_STD_OUTPUT; @@ -75,42 +77,28 @@ reg_mprj_io_18 = GPIO_MODE_MGMT_STD_OUTPUT; reg_mprj_io_17 = GPIO_MODE_MGMT_STD_OUTPUT; reg_mprj_io_16 = GPIO_MODE_MGMT_STD_OUTPUT; - reg_mprj_io_15 = GPIO_MODE_MGMT_STD_OUTPUT; + reg_mprj_io_15 = GPIO_MODE_MGMT_STD_INPUT_NOPULL; reg_mprj_io_14 = GPIO_MODE_MGMT_STD_OUTPUT; - reg_mprj_io_13 = GPIO_MODE_MGMT_STD_OUTPUT; - reg_mprj_io_12 = GPIO_MODE_MGMT_STD_OUTPUT; - reg_mprj_io_11 = GPIO_MODE_MGMT_STD_OUTPUT; - reg_mprj_io_10 = GPIO_MODE_MGMT_STD_OUTPUT; - reg_mprj_io_9 = GPIO_MODE_MGMT_STD_OUTPUT; - reg_mprj_io_8 = GPIO_MODE_MGMT_STD_OUTPUT; + - /* Apply configuration */ + /* Apply configuration */ reg_mprj_xfer = 1; while (reg_mprj_xfer == 1); - reg_la2_oenb = reg_la2_iena = 0x00000000; // [95:64] + reg_la2_oenb = reg_la2_iena = 0x00000000; // [95:64] // Flag start of the test - reg_mprj_datal = 0xAB600000; + reg_mprj_datal = 0xAB600000; - // sram_offset = 0x11223344; - // *((&sram_offset)+1) = 0x11234758; + /* UART Setup: */ + /* 8-N-1 115200B for 50Mhz System Clock */ + reg_UART_SETUP = 434; + - // if (sram_offset == 0x11223344){ - // if (*((&sram_offset)+1) == 0x11234758) - // reg_mprj_datal = 0x0000FF00; - // } + /* Send a data via UART */ + reg_UART_TX_DATA = 0x0000DE; - // Write software Write & Read Register - for(int i = 0; i < number_of_test; i++){ - *((&sram_offset)+i) = 0x11223344 + i; + if (reg_mprj_datal == 0xAB608000) { + reg_mprj_datal = 0xAB610000; } - - // reg_mprj_datal = 0x0000FF00 - (0x100 * number_of_test); - for(int i = 0; i < number_of_test; i++){ - if (*((&sram_offset)+i) != (0x11223344 + i)) - fail = 1; - } - if (fail == 0) - reg_mprj_datal = 0x0000FF00; }
diff --git a/verilog/dv/wb_port/wb_port_tb.v b/verilog/dv/wb_port/wb_port_tb.v index d3afb48..8bc519c 100644 --- a/verilog/dv/wb_port/wb_port_tb.v +++ b/verilog/dv/wb_port/wb_port_tb.v
@@ -17,44 +17,43 @@ `timescale 1 ns / 1 ps + module wb_port_tb; - reg clock; - reg RSTB; - reg CSB; - reg power1, power2; - reg power3, power4; + reg clock; + reg RSTB; + reg CSB; + reg power1, power2; + reg power3, power4; - wire gpio; - wire [37:0] mprj_io; - wire [15:0] checkbits; - wire [7:0] result; + wire gpio; + wire [37:0] mprj_io; + wire [7:0] mprj_io_0; + wire [15:0] checkbits; - assign checkbits = mprj_io[31:16]; - assign result = mprj_io[15:8]; + assign checkbits = mprj_io[31:16]; - assign mprj_io[3] = 1'b1; + assign mprj_io[3] = 1'b1; - // External clock is used by default. Make this artificially fast for the - // simulation. Normally this would be a slow clock and the digital PLL - // would be the fast clock. + // External clock is used by default. Make this artificially fast for the + // simulation. Normally this would be a slow clock and the digital PLL + // would be the fast clock. - always #10.0 clock <= (clock === 1'b0); + always #12.5 clock <= (clock === 1'b0); - initial begin - clock = 0; - end + initial begin + clock = 0; + end - initial begin - $dumpfile("wb_port.vcd"); - $dumpvars(0, wb_port_tb); + initial begin + $dumpfile("wb_port.vcd"); + $dumpvars(0, wb_port_tb); - // Repeat cycles of 1000 clock edges as needed to complete testbench - repeat (100) begin + // Repeat cycles of 1000 clock edges as needed to complete testbench + repeat (70) begin repeat (1000) @(posedge clock); - // $display("+1000 cycles"); - end + // $display("+1000 cycles"); + end $display("%c[1;31m",27); - $display ("MPRJ_IO value : 0x%0h", mprj_io[31:8]); `ifdef GL $display ("Monitor: Timeout, Test Mega-Project WB Port (GL) Failed"); `else @@ -64,87 +63,86 @@ $finish; end - initial begin - wait(checkbits == 16'hAB60); - $display("Monitor: MPRJ-Logic WB Started"); - wait(result == 8'hFF); - $display ("MPRJ_IO value : 0x%0h", mprj_io[31:0]); - `ifdef GL - $display("Monitor: Mega-Project WB (GL) Passed"); - `else - $display("Monitor: Mega-Project WB (RTL) Passed"); - `endif - $finish; - end + initial begin + wait(checkbits == 16'hAB60); + $display("Monitor: MPRJ-Logic WB Started"); + wait(checkbits == 16'hAB61); + `ifdef GL + $display("Monitor: Mega-Project WB (GL) Passed"); + `else + $display("Monitor: Mega-Project WB (RTL) Passed"); + `endif + $finish; + end - initial begin - RSTB <= 1'b0; - CSB <= 1'b1; // Force CSB high - #2000; - RSTB <= 1'b1; // Release reset - #100000; - CSB = 1'b0; // CSB can be released - end + initial begin + RSTB <= 1'b0; + CSB <= 1'b1; // Force CSB high + #2000; + RSTB <= 1'b1; // Release reset + #100000; + CSB = 1'b0; // CSB can be released + end - initial begin // Power-up sequence - power1 <= 1'b0; - power2 <= 1'b0; - #200; - power1 <= 1'b1; - #200; - power2 <= 1'b1; - end + initial begin // Power-up sequence + power1 <= 1'b0; + power2 <= 1'b0; + #200; + power1 <= 1'b1; + #200; + power2 <= 1'b1; + end - wire flash_csb; - wire flash_clk; - wire flash_io0; - wire flash_io1; + wire flash_csb; + wire flash_clk; + wire flash_io0; + wire flash_io1; - wire VDD3V3 = power1; - wire VDD1V8 = power2; - wire USER_VDD3V3 = power3; - wire USER_VDD1V8 = power4; - wire VSS = 1'b0; + wire VDD3V3 = power1; + wire VDD1V8 = power2; + wire USER_VDD3V3 = power3; + wire USER_VDD1V8 = power4; + wire VSS = 1'b0; - caravel uut ( - .vddio (VDD3V3), - .vddio_2 (VDD3V3), - .vssio (VSS), - .vssio_2 (VSS), - .vdda (VDD3V3), - .vssa (VSS), - .vccd (VDD1V8), - .vssd (VSS), - .vdda1 (VDD3V3), - .vdda1_2 (VDD3V3), - .vdda2 (VDD3V3), - .vssa1 (VSS), - .vssa1_2 (VSS), - .vssa2 (VSS), - .vccd1 (VDD1V8), - .vccd2 (VDD1V8), - .vssd1 (VSS), - .vssd2 (VSS), - .clock (clock), - .gpio (gpio), - .mprj_io (mprj_io), - .flash_csb(flash_csb), - .flash_clk(flash_clk), - .flash_io0(flash_io0), - .flash_io1(flash_io1), - .resetb (RSTB) - ); + caravel uut ( + .vddio (VDD3V3), + .vddio_2 (VDD3V3), + .vssio (VSS), + .vssio_2 (VSS), + .vdda (VDD3V3), + .vssa (VSS), + .vccd (VDD1V8), + .vssd (VSS), + .vdda1 (VDD3V3), + .vdda1_2 (VDD3V3), + .vdda2 (VDD3V3), + .vssa1 (VSS), + .vssa1_2 (VSS), + .vssa2 (VSS), + .vccd1 (VDD1V8), + .vccd2 (VDD1V8), + .vssd1 (VSS), + .vssd2 (VSS), + .clock (clock), + .gpio (gpio), + .mprj_io (mprj_io), + .flash_csb(flash_csb), + .flash_clk(flash_clk), + .flash_io0(flash_io0), + .flash_io1(flash_io1), + .resetb (RSTB) + ); - spiflash #( - .FILENAME("wb_port.hex") - ) spiflash ( - .csb(flash_csb), - .clk(flash_clk), - .io0(flash_io0), - .io1(flash_io1), - .io2(), // not used - .io3() // not used - ); + spiflash #( + .FILENAME("wb_port.hex") + ) spiflash ( + .csb(flash_csb), + .clk(flash_clk), + .io0(flash_io0), + .io1(flash_io1), + .io2(), // not used + .io3() // not used + ); endmodule `default_nettype wire \ No newline at end of file
diff --git a/verilog/includes/includes.rtl.caravel_user_project b/verilog/includes/includes.rtl.caravel_user_project index 2311771..acc27a6 100644 --- a/verilog/includes/includes.rtl.caravel_user_project +++ b/verilog/includes/includes.rtl.caravel_user_project
@@ -1,7 +1,13 @@ -# Caravel user project includes --v $(USER_PROJECT_VERILOG)/rtl/user_project_wrapper.v +# Caravel user project includes +-v $(USER_PROJECT_VERILOG)/rtl/user_project_wrapper.v -v $(USER_PROJECT_VERILOG)/rtl/user_proj_example.v -v $(USER_PROJECT_VERILOG)/rtl/sram/sram_wb_wrapper.sv -v $(USER_PROJECT_VERILOG)/rtl/wb_interconnect/wb_interconnect.sv -v $(USER_PROJECT_VERILOG)/rtl/wb_interconnect/wb_signal_reg.sv - +-v $(USER_PROJECT_VERILOG)/rtl/wbuart32/rxuart.v +-v $(USER_PROJECT_VERILOG)/rtl/wbuart32/txuart.v +-v $(USER_PROJECT_VERILOG)/rtl/wbuart32/rxuartlite.v +-v $(USER_PROJECT_VERILOG)/rtl/wbuart32/txuartlite.v +-v $(USER_PROJECT_VERILOG)/rtl/wbuart32/ufifo.v +-v $(USER_PROJECT_VERILOG)/rtl/wbuart32/skidbuffer.v +-v $(USER_PROJECT_VERILOG)/rtl/wbuart32/wbuart.v
diff --git a/verilog/includes/includes.rtl.secure-memory b/verilog/includes/includes.rtl.secure-memory new file mode 100644 index 0000000..278e739 --- /dev/null +++ b/verilog/includes/includes.rtl.secure-memory
@@ -0,0 +1,12 @@ +# Caravel user project includes +-v $(USER_PROJECT_VERILOG)/rtl/user_project_wrapper.v +-v $(USER_PROJECT_VERILOG)/rtl/user_proj_example.v +-v $(USER_PROJECT_VERILOG)/rtl/wb_interconnect/wb_interconnect.sv +-v $(USER_PROJECT_VERILOG)/rtl/wb_interconnect/wb_stagging.sv +-v $(USER_PROJECT_VERILOG)/rtl/wbuart32/rxuart.v +-v $(USER_PROJECT_VERILOG)/rtl/wbuart32/txuart.v +-v $(USER_PROJECT_VERILOG)/rtl/wbuart32/rxuartlite.v +-v $(USER_PROJECT_VERILOG)/rtl/wbuart32/txuartlite.v +-v $(USER_PROJECT_VERILOG)/rtl/wbuart32/ufifo.v +-v $(USER_PROJECT_VERILOG)/rtl/wbuart32/skidbuffer.v +-v $(USER_PROJECT_VERILOG)/rtl/wbuart32/wbuart.v \ No newline at end of file
diff --git a/verilog/rtl/uprj_netlists.v b/verilog/rtl/uprj_netlists.v index 4a1dec9..3537de8 100644 --- a/verilog/rtl/uprj_netlists.v +++ b/verilog/rtl/uprj_netlists.v
@@ -25,7 +25,4 @@ `else `include "user_project_wrapper.v" `include "user_proj_example.v" - `include "wb_interconnect/wb_interconnect.sv" - `include "wb_interconnect/wb_signal_reg.sv" - `include "sram/sram_wb_wrapper.sv" -`endif +`endif \ No newline at end of file
diff --git a/verilog/rtl/user_proj_example.v b/verilog/rtl/user_proj_example.v index 3222ea0..e9be131 100644 --- a/verilog/rtl/user_proj_example.v +++ b/verilog/rtl/user_proj_example.v
@@ -75,6 +75,11 @@ parameter SRAM_ADDR_START = 9'h000; parameter SRAM_ADDR_END = 9'h1F8; +parameter UART_ADDR_WD = 9; +parameter UART_DATA_WD = 32; +parameter UART_ADDR_START = 9'h000; +parameter UART_ADDR_END = 9'h1F8; + //--------------------------------------------------------------------- // WB Master Interface //--------------------------------------------------------------------- @@ -95,6 +100,20 @@ wire [SRAM_DATA_WD-1:0] s0_wb_dat_o; wire s0_wb_ack_o; +//--------------------------------------------------------------------- +// UART +//--------------------------------------------------------------------- +wire s1_wb_cyc_i; +wire s1_wb_stb_i; +wire [UART_ADDR_WD-1:0] s1_wb_adr_i; +wire s1_wb_we_i; +wire [UART_DATA_WD-1:0] s1_wb_dat_i; +wire [UART_DATA_WD/8-1:0] s1_wb_sel_i; +wire [UART_DATA_WD-1:0] s1_wb_dat_o; +wire s1_wb_ack_o; + + + wb_interconnect interconnect ( `ifdef USE_POWER_PINS @@ -125,14 +144,14 @@ .s0_wb_stb_o(s0_wb_stb_i) // Slave 1 Interface - // .s1_wb_dat_i(), - // .s1_wb_ack_i(), - // .s1_wb_dat_o(), - // .s1_wb_adr_o(), - // .s1_wb_sel_o(), - // .s1_wb_we_o (), - // .s1_wb_cyc_o(), - // .s1_wb_stb_o(), + .s1_wb_dat_i(s1_wb_dat_o), + .s1_wb_ack_i(s1_wb_ack_o), + .s1_wb_dat_o(s1_wb_dat_i), + .s1_wb_adr_o(s1_wb_adr_i), + .s1_wb_sel_o(s1_wb_sel_i), + .s1_wb_we_o (s1_wb_we_i)), + .s1_wb_cyc_o(s1_wb_cyc_i), + .s1_wb_stb_o(s1_wb_stb_i), // Slave 2 Interface // .s2_wb_dat_i(), @@ -177,5 +196,35 @@ .wb_ack_o(s0_wb_ack_o) // acknowlegement ); + +wbuart +#( + .INITIAL_SETUP(31'd434 ), // 115200 baudrate for 50MHz clock + .LGFLEN(4'h4 ), + .HARDWARE_FLOW_CONTROL_PRESENT(1'b0 ) +) +wbuart_dut ( + .i_clk (wb_clk_i ), + .i_reset (wb_rst_i ), + .i_wb_cyc (s1_wb_cyc_i ), + .i_wb_stb (s1_wb_stb_i ), + .i_wb_we (s1_wb_we_i ), + .i_wb_addr (s1_wb_adr_i ), + .i_wb_data (s1_wb_dat_i ), + .i_wb_sel (s1_wb_sel_i ), + .o_wb_stall ( ), + .o_wb_ack (s1_wb_ack_o ), + .o_wb_data (s1_wb_dat_o ), + .i_uart_rx (io_in[15] ), + .o_uart_tx (io_out[14] ), + .i_cts_n (1'b0 ), + .o_rts_n ( ), + .o_uart_rx_int ( ), + .o_uart_tx_int ( ), + .o_uart_rxfifo_int ( ), + .o_uart_txfifo_int ( ) +); + + endmodule `default_nettype wire
diff --git a/verilog/rtl/wb_interconnect/wb_interconnect.sv b/verilog/rtl/wb_interconnect/wb_interconnect.sv index 05912ce..6397bc6 100644 --- a/verilog/rtl/wb_interconnect/wb_interconnect.sv +++ b/verilog/rtl/wb_interconnect/wb_interconnect.sv
@@ -53,14 +53,14 @@ output wire s0_wb_stb_o // Slave 1 Interface - // input logic [31:0] s1_wb_dat_i, - // input logic s1_wb_ack_i, - // output wire [31:0] s1_wb_dat_o, - // output wire [8:0] s1_wb_adr_o, - // output wire [3:0] s1_wb_sel_o, - // output wire s1_wb_we_o, - // output wire s1_wb_cyc_o, - // output wire s1_wb_stb_o, + input logic [31:0] s1_wb_dat_i, + input logic s1_wb_ack_i, + output wire [31:0] s1_wb_dat_o, + output wire [8:0] s1_wb_adr_o, + output wire [3:0] s1_wb_sel_o, + output wire s1_wb_we_o, + output wire s1_wb_cyc_o, + output wire s1_wb_stb_o, // Slave 2 Interface // input logic [31:0] s2_wb_dat_i, @@ -96,17 +96,17 @@ logic [31:0] m0_wb_dat_o_reg; logic m0_wb_ack_reg; -// wire [31:0] s_bus_rd_wb_dat = (m0_wb_adr_i[13:12] == 2'b00) ? s0_wb_dat_i : -// (m0_wb_adr_i[13:12] == 2'b01) ? s1_wb_dat_i : -// (m0_wb_adr_i[13:12] == 2'b10) ? s2_wb_dat_i : -// s3_wb_dat_i; -// wire s_bus_rd_wb_ack = (m0_wb_adr_i[13:12] == 2'b00) ? s0_wb_ack_i : -// (m0_wb_adr_i[13:12] == 2'b01) ? s1_wb_ack_i : -// (m0_wb_adr_i[13:12] == 2'b10) ? s2_wb_ack_i : -// s3_wb_ack_i; +wire [31:0] s_bus_rd_wb_dat = (m0_wb_adr_i[13:12] == 2'b00) ? s0_wb_dat_i : + (m0_wb_adr_i[13:12] == 2'b01) ? s1_wb_dat_i : + (m0_wb_adr_i[13:12] == 2'b10) ? s2_wb_dat_i : + s3_wb_dat_i; +wire s_bus_rd_wb_ack = (m0_wb_adr_i[13:12] == 2'b00) ? s0_wb_ack_i : + (m0_wb_adr_i[13:12] == 2'b01) ? s1_wb_ack_i : + (m0_wb_adr_i[13:12] == 2'b10) ? s2_wb_ack_i : + s3_wb_ack_i; -wire [31:0] s_bus_rd_wb_dat = s0_wb_dat_i; -wire s_bus_rd_wb_ack = s0_wb_ack_i; +//wire [31:0] s_bus_rd_wb_dat = s0_wb_dat_i; +//wire s_bus_rd_wb_ack = s0_wb_ack_i; //------------------------------------------------------------------- // EXTERNAL MEMORY MAP @@ -120,26 +120,26 @@ //---------------------------------------- // Slave Mapping //--------------------------------------- -assign s0_wb_dat_o = m0_wb_dat_i_reg; -assign s0_wb_adr_o = m0_wb_adr_reg[8:0]; -assign s0_wb_sel_o = m0_wb_sel_reg; -assign s0_wb_we_o = m0_wb_we_reg; -assign s0_wb_cyc_o = m0_wb_cyc_reg; -assign s0_wb_stb_o = m0_wb_stb_reg; +//assign s0_wb_dat_o = m0_wb_dat_i_reg; +//assign s0_wb_adr_o = m0_wb_adr_reg[8:0]; +//assign s0_wb_sel_o = m0_wb_sel_reg; +//assign s0_wb_we_o = m0_wb_we_reg; +//assign s0_wb_cyc_o = m0_wb_cyc_reg; +//assign s0_wb_stb_o = m0_wb_stb_reg; -// assign s0_wb_dat_o = (m0_wb_tid_reg == 2'b00) ? m0_wb_dat_i_reg : 2'b00; -// assign s0_wb_adr_o = (m0_wb_tid_reg == 2'b00) ? m0_wb_adr_reg : 2'b00; -// assign s0_wb_sel_o = (m0_wb_tid_reg == 2'b00) ? m0_wb_sel_reg : 2'b00; -// assign s0_wb_we_o = (m0_wb_tid_reg == 2'b00) ? m0_wb_we_reg : 2'b00; -// assign s0_wb_cyc_o = (m0_wb_tid_reg == 2'b00) ? m0_wb_cyc_reg : 2'b00; -// assign s0_wb_stb_o = (m0_wb_tid_reg == 2'b00) ? m0_wb_stb_reg : 2'b00; +assign s0_wb_dat_o = (m0_wb_tid_reg == 2'b00) ? m0_wb_dat_i_reg : 2'b00; +assign s0_wb_adr_o = (m0_wb_tid_reg == 2'b00) ? m0_wb_adr_reg : 2'b00; +assign s0_wb_sel_o = (m0_wb_tid_reg == 2'b00) ? m0_wb_sel_reg : 2'b00; +assign s0_wb_we_o = (m0_wb_tid_reg == 2'b00) ? m0_wb_we_reg : 2'b00; +assign s0_wb_cyc_o = (m0_wb_tid_reg == 2'b00) ? m0_wb_cyc_reg : 2'b00; +assign s0_wb_stb_o = (m0_wb_tid_reg == 2'b00) ? m0_wb_stb_reg : 2'b00; -// assign s1_wb_dat_o = (m0_wb_tid_reg == 2'b01) ? m0_wb_dat_i_reg : 2'b00; -// assign s1_wb_adr_o = (m0_wb_tid_reg == 2'b01) ? m0_wb_adr_reg : 2'b00; -// assign s1_wb_sel_o = (m0_wb_tid_reg == 2'b01) ? m0_wb_sel_reg : 2'b00; -// assign s1_wb_we_o = (m0_wb_tid_reg == 2'b01) ? m0_wb_we_reg : 2'b00; -// assign s1_wb_cyc_o = (m0_wb_tid_reg == 2'b01) ? m0_wb_cyc_reg : 2'b00; -// assign s1_wb_stb_o = (m0_wb_tid_reg == 2'b01) ? m0_wb_stb_reg : 2'b00; +assign s1_wb_dat_o = (m0_wb_tid_reg == 2'b01) ? m0_wb_dat_i_reg : 2'b00; +assign s1_wb_adr_o = (m0_wb_tid_reg == 2'b01) ? m0_wb_adr_reg : 2'b00; +assign s1_wb_sel_o = (m0_wb_tid_reg == 2'b01) ? m0_wb_sel_reg : 2'b00; +assign s1_wb_we_o = (m0_wb_tid_reg == 2'b01) ? m0_wb_we_reg : 2'b00; +assign s1_wb_cyc_o = (m0_wb_tid_reg == 2'b01) ? m0_wb_cyc_reg : 2'b00; +assign s1_wb_stb_o = (m0_wb_tid_reg == 2'b01) ? m0_wb_stb_reg : 2'b00; // assign s2_wb_dat_o = (m0_wb_tid_reg == 2'b10) ? m0_wb_dat_i_reg : 2'b00; // assign s2_wb_adr_o = (m0_wb_tid_reg == 2'b10) ? m0_wb_adr_reg : 2'b00;
diff --git a/verilog/rtl/wbuart32/axiluart.v b/verilog/rtl/wbuart32/axiluart.v new file mode 100644 index 0000000..2b7f133 --- /dev/null +++ b/verilog/rtl/wbuart32/axiluart.v
@@ -0,0 +1,842 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Filename: axiluart +// {{{ +// Project: wbuart32, a full featured UART with simulator +// +// Purpose: A basic AXI-Lite serial port controller. It has the same +// interface as the WBUART core in the same directory. +// +// Creator: Dan Gisselquist, Ph.D. +// Gisselquist Technology, LLC +// +//////////////////////////////////////////////////////////////////////////////// +// }}} +// Copyright (C) 2020-2021, Gisselquist Technology, LLC +// {{{ +// +// This program is free software (firmware): you can redistribute it and/or +// modify it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program. (It's in the $(ROOT)/doc directory. Run make with no +// target there if the PDF file isn't present.) If not, see +// <http://www.gnu.org/licenses/> for a copy. +// +// License: GPL, v3, as defined and found on www.gnu.org, +// http://www.gnu.org/licenses/gpl.html +// +// +//////////////////////////////////////////////////////////////////////////////// +// }}} +// +`default_nettype none +// +module axiluart #( + // {{{ + // 4MB 8N1, when using 100MHz clock + parameter [30:0] INITIAL_SETUP = 31'd25, + // + // LGFLEN: The log (based two) of our FIFOs size. Maxes out + // at 10, representing a FIFO length of 1024. + parameter [3:0] LGFLEN = 4, + // + // HARDWARE_FLOW_CONTROL_PRESET controls whether or not we + // ignore the RTS/CTS signaling. If present, we only start + // transmitting if + parameter [0:0] HARDWARE_FLOW_CONTROL_PRESENT = 1'b1, + // Perform a simple/quick bounds check on the log FIFO length, + // to make sure its within the bounds we can support with our + // current interface. + localparam [3:0] LCLLGFLEN = (LGFLEN > 4'ha)? 4'ha + : ((LGFLEN < 4'h2) ? 4'h2 : LGFLEN), + // + // Size of the AXI-lite bus. These are fixed, since 1) AXI-lite + // is fixed at a width of 32-bits by Xilinx def'n, and 2) since + // we only ever have 4 configuration words. + parameter C_AXI_ADDR_WIDTH = 4, + localparam C_AXI_DATA_WIDTH = 32, + parameter [0:0] OPT_SKIDBUFFER = 1'b0, + parameter [0:0] OPT_LOWPOWER = 0, + localparam ADDRLSB = $clog2(C_AXI_DATA_WIDTH)-3 + // }}} + ) ( + // AXI-lite signaling + // {{{ + input wire S_AXI_ACLK, + input wire S_AXI_ARESETN, + // + input wire S_AXI_AWVALID, + output wire S_AXI_AWREADY, + input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_AWADDR, + input wire [2:0] S_AXI_AWPROT, + // + input wire S_AXI_WVALID, + output wire S_AXI_WREADY, + input wire [C_AXI_DATA_WIDTH-1:0] S_AXI_WDATA, + input wire [C_AXI_DATA_WIDTH/8-1:0] S_AXI_WSTRB, + // + output wire S_AXI_BVALID, + input wire S_AXI_BREADY, + output wire [1:0] S_AXI_BRESP, + // + input wire S_AXI_ARVALID, + output wire S_AXI_ARREADY, + input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_ARADDR, + input wire [2:0] S_AXI_ARPROT, + // + output wire S_AXI_RVALID, + input wire S_AXI_RREADY, + output wire [C_AXI_DATA_WIDTH-1:0] S_AXI_RDATA, + output wire [1:0] S_AXI_RRESP, + // }}} + // UART signals + // {{{ + input wire i_uart_rx, + output wire o_uart_tx, + // + // CTS is the "Clear-to-send" hardware flow control signal. We + // set it anytime our FIFO isn't full. Feel free to ignore + // this output if you do not wish to use flow control. + input wire i_cts_n, + // + // RTS is used for hardware flow control. According to + // Wikipedia, it should probably be renamed RTR for "ready to + // receive". It tell us whether or not the receiving hardware + // is ready to accept another byte. If low, the transmitter + // will pause. + // + // If you don't wish to use hardware flow control, just set + // HARDWARE_FLOW_CONTROL_PRESENT to 1'b0 and let the optimizer + // simply remove this logic. + output reg o_rts_n, + // }}} + // A series of outgoing interrupts to select from among + // {{{ + output wire o_uart_rx_int, + output wire o_uart_tx_int, + output wire o_uart_rxfifo_int, + output wire o_uart_txfifo_int + // }}} + ); + + //////////////////////////////////////////////////////////////////////// + // + // Register/wire signal declarations + // + //////////////////////////////////////////////////////////////////////// + // + // {{{ + wire i_reset = !S_AXI_ARESETN; + + wire axil_write_ready; + wire [C_AXI_ADDR_WIDTH-ADDRLSB-1:0] awskd_addr; + // + wire [C_AXI_DATA_WIDTH-1:0] wskd_data; + wire [C_AXI_DATA_WIDTH/8-1:0] wskd_strb; + reg axil_bvalid; + // + wire axil_read_ready; + wire [C_AXI_ADDR_WIDTH-ADDRLSB-1:0] arskd_addr; + reg [C_AXI_DATA_WIDTH-1:0] axil_read_data; + reg axil_read_valid; + // + // + wire tx_busy; + // + reg [30:0] uart_setup; + // + wire rx_stb, rx_break, rx_perr, rx_ferr, ck_uart; + wire [7:0] rx_uart_data; + reg rx_uart_reset; + // + wire rx_empty_n, rx_fifo_err; + wire [7:0] rxf_axil_data; + wire [15:0] rxf_status; + reg rxf_axil_read; + reg r_rx_perr, r_rx_ferr; + // + wire [(LCLLGFLEN-1):0] check_cutoff; + wire [31:0] axil_rx_data; + // + wire tx_empty_n, txf_err, tx_break; + wire [7:0] tx_data; + wire [15:0] txf_status; + reg txf_axil_write, tx_uart_reset; + reg [7:0] txf_axil_data; + wire [31:0] axil_tx_data; + wire [31:0] axil_fifo_data; + // + reg [1:0] r_axil_addr; + reg r_preread; + + reg [31:0] new_setup; + + // }}} + //////////////////////////////////////////////////////////////////////// + // + // AXI-lite signaling + // + //////////////////////////////////////////////////////////////////////// + // + // {{{ + + // + // Write signaling + // + // {{{ + + generate if (OPT_SKIDBUFFER) + begin : SKIDBUFFER_WRITE + + wire awskd_valid, wskd_valid; + + skidbuffer #(.OPT_OUTREG(0), + .OPT_LOWPOWER(OPT_LOWPOWER), + .DW(C_AXI_ADDR_WIDTH-ADDRLSB)) + axilawskid(// + .i_clk(S_AXI_ACLK), .i_reset(i_reset), + .i_valid(S_AXI_AWVALID), .o_ready(S_AXI_AWREADY), + .i_data(S_AXI_AWADDR[C_AXI_ADDR_WIDTH-1:ADDRLSB]), + .o_valid(awskd_valid), .i_ready(axil_write_ready), + .o_data(awskd_addr)); + + skidbuffer #(.OPT_OUTREG(0), + .OPT_LOWPOWER(OPT_LOWPOWER), + .DW(C_AXI_DATA_WIDTH+C_AXI_DATA_WIDTH/8)) + axilwskid(// + .i_clk(S_AXI_ACLK), .i_reset(i_reset), + .i_valid(S_AXI_WVALID), .o_ready(S_AXI_WREADY), + .i_data({ S_AXI_WDATA, S_AXI_WSTRB }), + .o_valid(wskd_valid), .i_ready(axil_write_ready), + .o_data({ wskd_data, wskd_strb })); + + assign axil_write_ready = awskd_valid && wskd_valid + && (!S_AXI_BVALID || S_AXI_BREADY); + + end else begin : SIMPLE_WRITES + + reg axil_awready; + + initial axil_awready = 1'b0; + always @(posedge S_AXI_ACLK) + if (!S_AXI_ARESETN) + axil_awready <= 1'b0; + else + axil_awready <= !axil_awready + && (S_AXI_AWVALID && S_AXI_WVALID) + && (!S_AXI_BVALID || S_AXI_BREADY); + + assign S_AXI_AWREADY = axil_awready; + assign S_AXI_WREADY = axil_awready; + + assign awskd_addr = S_AXI_AWADDR[C_AXI_ADDR_WIDTH-1:ADDRLSB]; + assign wskd_data = S_AXI_WDATA; + assign wskd_strb = S_AXI_WSTRB; + + assign axil_write_ready = axil_awready; + + end endgenerate + + initial axil_bvalid = 0; + always @(posedge S_AXI_ACLK) + if (i_reset) + axil_bvalid <= 0; + else if (axil_write_ready) + axil_bvalid <= 1; + else if (S_AXI_BREADY) + axil_bvalid <= 0; + + assign S_AXI_BVALID = axil_bvalid; + assign S_AXI_BRESP = 2'b00; + // }}} + + // + // Read signaling + // + // {{{ + + generate if (OPT_SKIDBUFFER) + begin : SKIDBUFFER_READ + + wire arskd_valid; + + skidbuffer #(.OPT_OUTREG(0), + .OPT_LOWPOWER(OPT_LOWPOWER), + .DW(C_AXI_ADDR_WIDTH-ADDRLSB)) + axilarskid(// + .i_clk(S_AXI_ACLK), .i_reset(i_reset), + .i_valid(S_AXI_ARVALID), .o_ready(S_AXI_ARREADY), + .i_data(S_AXI_ARADDR[C_AXI_ADDR_WIDTH-1:ADDRLSB]), + .o_valid(arskd_valid), .i_ready(axil_read_ready), + .o_data(arskd_addr)); + + // High bandwidth reads + assign axil_read_ready = arskd_valid + && (!r_preread || !axil_read_valid + || S_AXI_RREADY); + + end else begin : SIMPLE_READS + + reg axil_arready; + + initial axil_arready = 1; + always @(posedge S_AXI_ACLK) + if (!S_AXI_ARESETN) + axil_arready <= 1; + else if (S_AXI_ARVALID && S_AXI_ARREADY) + axil_arready <= 0; + else if (S_AXI_RVALID && S_AXI_RREADY) + axil_arready <= 1; + + assign arskd_addr = S_AXI_ARADDR[C_AXI_ADDR_WIDTH-1:ADDRLSB]; + assign S_AXI_ARREADY = axil_arready; + assign axil_read_ready = (S_AXI_ARVALID && S_AXI_ARREADY); + + end endgenerate + + initial axil_read_valid = 1'b0; + always @(posedge S_AXI_ACLK) + if (i_reset) + axil_read_valid <= 1'b0; + else if (r_preread) + axil_read_valid <= 1'b1; + else if (S_AXI_RREADY) + axil_read_valid <= 1'b0; + + assign S_AXI_RVALID = axil_read_valid; + assign S_AXI_RDATA = axil_read_data; + assign S_AXI_RRESP = 2'b00; + // }}} + + // }}} + //////////////////////////////////////////////////////////////////////// + // + // AXI-lite register logic + // + //////////////////////////////////////////////////////////////////////// + // + // {{{ + + localparam [1:0] UART_SETUP = 2'b00, + UART_FIFO = 2'b01, + UART_RXREG = 2'b10, + UART_TXREG = 2'b11; + + always @(*) + new_setup = apply_wstrb({1'b0,uart_setup},wskd_data,wskd_strb); + + // + // The UART setup parameters: bits per byte, stop bits, parity, and + // baud rate are all captured within this uart_setup register. + // + initial uart_setup = INITIAL_SETUP + | ((HARDWARE_FLOW_CONTROL_PRESENT==1'b0)? 31'h40000000 : 0); + always @(posedge S_AXI_ACLK) + if ((axil_write_ready)&&(awskd_addr == UART_SETUP)) + begin + uart_setup <= new_setup[30:0]; + + if (!HARDWARE_FLOW_CONTROL_PRESENT) + uart_setup[30] <= 1'b1; + end + + ///////////////////////////////////////// + // + // First, the UART receiver + // {{{ + ///////////////////////////////////////// + // + // + + + // Here's our UART receiver. Basically, it accepts our setup wires, + // the UART input, a clock, and a reset line, and produces outputs: + // a stb (true when new data is ready), and an 8-bit data out value + // valid when stb is high. +`ifdef FORMAL + (* anyseq *) reg w_rx_break, w_rx_perr, w_rx_ferr, w_ck_uart; + assign rx_break = w_rx_break; + assign w_rx_perr = w_rx_perr; + assign w_rx_ferr = w_rx_ferr; + assign ck_uart = w_ck_uart; +`else +`ifdef USE_LITE_UART + rxuartlite #(.CLOCKS_PER_BAUD(INITIAL_SETUP[23:0])) + rx(S_AXI_ACLK, i_uart_rx, rx_stb, rx_uart_data); + assign rx_break = 1'b0; + assign rx_perr = 1'b0; + assign rx_ferr = 1'b0; + assign ck_uart = 1'b0; +`else + // The full receiver also produces a break value (true during a break + // cond.), and parity/framing error flags--also valid when stb is true. + rxuart #(.INITIAL_SETUP(INITIAL_SETUP)) rx(S_AXI_ACLK, (!S_AXI_ARESETN)||(rx_uart_reset), + uart_setup, i_uart_rx, + rx_stb, rx_uart_data, rx_break, + rx_perr, rx_ferr, ck_uart); + // The real trick is ... now that we have this extra data, what do we do + // with it? +`endif +`endif // FORMAL + + // We place it into a receiver FIFO. + // + // Note that the FIFO will be cleared upon any reset: either if there's + // a UART break condition on the line, the receiver is in reset, or an + // external reset is issued. + // + // The FIFO accepts strobe and data from the receiver. + // We issue another wire to it (rxf_axil_read), true when we wish to + // read from the FIFO, and we get our data in rxf_axil_data. The FIFO + // outputs four status-type values: 1) is it non-empty, 2) is the FIFO + // over half full, 3) a 16-bit status register, containing info + // regarding how full the FIFO truly is, and 4) an error indicator. + ufifo #(.LGFLEN(LCLLGFLEN), .RXFIFO(1)) + rxfifo(S_AXI_ACLK, (!S_AXI_ARESETN)||(rx_break)||(rx_uart_reset), + rx_stb, rx_uart_data, + rx_empty_n, + rxf_axil_read, rxf_axil_data, + rxf_status, rx_fifo_err); + assign o_uart_rxfifo_int = rxf_status[1]; + + // We produce four interrupts. One of the receive interrupts indicates + // whether or not the receive FIFO is non-empty. This should wake up + // the CPU. + assign o_uart_rx_int = rxf_status[0]; + + // The clear to send line, which may be ignored, but which we set here + // to be true any time the FIFO has fewer than N-2 items in it. + // Why not N-1? Because at N-1 we are totally full, but already so full + // that if the transmit end starts sending we won't have a location to + // receive it. (Transmit might've started on the next character by the + // time we set this--thus we need to set it to one, one character before + // necessary). + assign check_cutoff = -3; + always @(posedge S_AXI_ACLK) + o_rts_n <= ((HARDWARE_FLOW_CONTROL_PRESENT) + &&(!uart_setup[30]) + &&(rxf_status[(LCLLGFLEN+1):2] > check_cutoff)); + + // If the bus requests that we read from the receive FIFO, we need to + // tell this to the receive FIFO. Note that because we are using a + // clock here, the output from the receive FIFO will necessarily be + // delayed by an extra clock. + initial rxf_axil_read = 1'b0; + always @(posedge S_AXI_ACLK) + rxf_axil_read<=(axil_read_ready)&&(arskd_addr[1:0]==UART_RXREG); + + // Now, let's deal with those RX UART errors: both the parity and frame + // errors. As you may recall, these are valid only when rx_stb is + // valid, so we need to hold on to them until the user reads them via + // a UART read request.. + initial r_rx_perr = 1'b0; + initial r_rx_ferr = 1'b0; + always @(posedge S_AXI_ACLK) + if ((rx_uart_reset)||(rx_break)) + begin + // Clear the error + r_rx_perr <= 1'b0; + r_rx_ferr <= 1'b0; + end else if (axil_write_ready&&awskd_addr == UART_RXREG && wskd_strb[1]) + begin + // Reset the error lines if a '1' is ever written to + // them, otherwise leave them alone. + // + r_rx_perr <= (r_rx_perr)&&(!wskd_data[9]); + r_rx_ferr <= (r_rx_ferr)&&(!wskd_data[10]); + end else if (rx_stb) + begin + // On an rx_stb, capture any parity or framing error + // indications. These aren't kept with the data rcvd, + // but rather kept external to the FIFO. As a result, + // if you get a parity or framing error, you will never + // know which data byte it was associated with. + // For now ... that'll work. + r_rx_perr <= (r_rx_perr)||(rx_perr); + r_rx_ferr <= (r_rx_ferr)||(rx_ferr); + end + + initial rx_uart_reset = 1'b1; + always @(posedge S_AXI_ACLK) + if ((!S_AXI_ARESETN)||((axil_write_ready)&&(awskd_addr[1:0]== UART_SETUP) && (&wskd_strb))) + // The receiver reset, always set on a master reset + // request. + rx_uart_reset <= 1'b1; + else if (axil_write_ready&&(awskd_addr[1:0]==UART_RXREG)&&wskd_strb[1]) + // Writes to the receive register will command a receive + // reset anytime bit[12] is set. + rx_uart_reset <= wskd_data[12]; + else + rx_uart_reset <= 1'b0; + + // Finally, we'll construct a 32-bit value from these various wires, + // to be returned over the bus on any read. These include the data + // that would be read from the FIFO, an error indicator set upon + // reading from an empty FIFO, a break indicator, and the frame and + // parity error signals. + assign axil_rx_data = { 16'h00, + 3'h0, rx_fifo_err, + rx_break, rx_ferr, r_rx_perr, !rx_empty_n, + rxf_axil_data}; + + // }}} + ///////////////////////////////////////// + // + // Then the UART transmitter + // {{{ + ///////////////////////////////////////// + // + // Unlike the receiver which goes from RXUART -> UFIFO -> WB, the + // transmitter basically goes WB -> UFIFO -> TXUART. Hence, to build + // support for the transmitter, we start with the command to write data + // into the FIFO. In this case, we use the act of writing to the + // UART_TXREG address as our indication that we wish to write to the + // FIFO. Here, we create a write command line, and latch the data for + // the extra clock that it'll take so that the command and data can be + // both true on the same clock. + initial txf_axil_write = 1'b0; + always @(posedge S_AXI_ACLK) + begin + txf_axil_write <= (axil_write_ready)&&(awskd_addr == UART_TXREG) + && wskd_strb[0]; + txf_axil_data <= wskd_data[7:0]; + end + + // Transmit FIFO + // + // Most of this is just wire management. The TX FIFO is identical in + // implementation to the RX FIFO (theyre both UFIFOs), but the TX + // FIFO is fed from the WB and read by the transmitter. Some key + // differences to note: we reset the transmitter on any request for a + // break. We read from the FIFO any time the UART transmitter is idle. + // and ... we just set the values (above) for controlling writing into + // this. + ufifo #(.LGFLEN(LGFLEN), .RXFIFO(0)) + txfifo(S_AXI_ACLK, (tx_break)||(tx_uart_reset), + txf_axil_write, txf_axil_data, + tx_empty_n, + (!tx_busy)&&(tx_empty_n), tx_data, + txf_status, txf_err); + // Let's create two transmit based interrupts from the FIFO for the CPU. + // The first will be true any time the FIFO has at least one open + // position within it. + assign o_uart_tx_int = txf_status[0]; + // The second will be true any time the FIFO is less than half + // full, allowing us a change to always keep it (near) fully + // charged. + assign o_uart_txfifo_int = txf_status[1]; + +`ifndef USE_LITE_UART + // Break logic + // + // A break in a UART controller is any time the UART holds the line + // low for an extended period of time. Here, we capture the + // wskd_data[9] wire, on writes, as an indication we wish to break. + // As long as you write unsigned characters to the interface, this + // will never be true unless you wish it to be true. Be aware, though, + // writing a valid value to the interface will bring it out of the + // break condition. + reg r_tx_break; + initial r_tx_break = 1'b0; + always @(posedge S_AXI_ACLK) + if (!S_AXI_ARESETN) + r_tx_break <= 1'b0; + else if (axil_write_ready &&(awskd_addr[1:0]== UART_TXREG) && + wskd_strb[1]) + r_tx_break <= wskd_data[9]; + assign tx_break = r_tx_break; +`else + assign tx_break = 1'b0; +`endif + + // TX-Reset logic + // + // This is nearly identical to the RX reset logic above. Basically, + // any time someone writes to bit [12] the transmitter will go through + // a reset cycle. Keep bit [12] low, and everything will proceed as + // normal. + initial tx_uart_reset = 1'b1; + always @(posedge S_AXI_ACLK) + if ((!S_AXI_ARESETN)||((axil_write_ready)&&(awskd_addr == UART_SETUP))) + tx_uart_reset <= 1'b1; + else if ((axil_write_ready)&&(awskd_addr[1:0]== UART_TXREG) && wskd_strb[1]) + tx_uart_reset <= wskd_data[12]; + else + tx_uart_reset <= 1'b0; + +`ifdef FORMAL + (* anyseq *) reg w_uart_tx, w_tx_busy; + assign tx_busy = w_uart_tx; + assign o_uart_tx = w_uart_tx; +`else +`ifdef USE_LITE_UART + txuartlite #(.CLOCKS_PER_BAUD(INITIAL_SETUP[23:0])) tx(S_AXI_ACLK, (tx_empty_n), tx_data, + o_uart_tx, tx_busy); +`else + wire cts_n; + assign cts_n = (HARDWARE_FLOW_CONTROL_PRESENT)&&(i_cts_n); + + // Finally, the UART transmitter module itself. Note that we haven't + // connected the reset wire. Transmitting is as simple as setting + // the stb value (here set to tx_empty_n) and the data. When these + // are both set on the same clock that tx_busy is low, the transmitter + // will move on to the next data byte. Really, the only thing magical + // here is that tx_empty_n wire--thus, if there's anything in the FIFO, + // we read it here. (You might notice above, we register a read any + // time (tx_empty_n) and (!tx_busy) are both true---the condition for + // starting to transmit a new byte.) + txuart #(.INITIAL_SETUP(INITIAL_SETUP)) tx(S_AXI_ACLK, 1'b0, uart_setup, + r_tx_break, (tx_empty_n), tx_data, + cts_n, o_uart_tx, tx_busy); +`endif +`endif // FORMAL + + // Now that we are done with the chain, pick some wires for the user + // to read on any read of the transmit port. + // + // This port is different from reading from the receive port, since + // there are no side effects. (Reading from the receive port advances + // the receive FIFO, here only writing to the transmit port advances the + // transmit FIFO--hence the read values are free for ... whatever.) + // We choose here to provide information about the transmit FIFO + // (txf_err, txf_half_full, txf_full_n), information about the current + // voltage on the line (o_uart_tx)--and even the voltage on the receive + // line (ck_uart), as well as our current setting of the break and + // whether or not we are actively transmitting. + assign axil_tx_data = { 16'h00, + i_cts_n, txf_status[1:0], txf_err, + ck_uart, o_uart_tx, tx_break, (tx_busy|txf_status[0]), + (tx_busy|txf_status[0])?txf_axil_data:8'b00}; + // }}} + + ///////////////////////////////////////// + // + // FIFO return + // {{{ + ///////////////////////////////////////// + // + // Each of the FIFO's returns a 16 bit status value. This value tells + // us both how big the FIFO is, as well as how much of the FIFO is in + // use. Let's merge those two status words together into a word we + // can use when reading about the FIFO. + assign axil_fifo_data = { txf_status, rxf_status }; + + // }}} + ///////////////////////////////////////// + // + // Final read register + // {{{ + ///////////////////////////////////////// + // + // You may recall from above that reads take two clocks. Hence, we + // need to delay the address decoding for a clock until the data is + // ready. We do that here. + initial r_preread = 0; + always @(posedge S_AXI_ACLK) + if (!S_AXI_ARESETN) + r_preread <= 0; + else if (axil_read_ready) + r_preread <= 1; + else if (!S_AXI_RVALID || S_AXI_RREADY) + r_preread <= 0; + + always @(posedge S_AXI_ACLK) + if (axil_read_ready) + r_axil_addr <= arskd_addr; + + // Finally, set the return data. This data must be valid on the same + // clock S_AXI_RVALID is high. On all other clocks, it is + // irrelelant--since no one cares, no one is reading it, it gets lost + // in the mux in the interconnect, etc. For this reason, we can just + // simplify our logic. + always @(posedge S_AXI_ACLK) + if (!S_AXI_RVALID || S_AXI_RREADY) + begin + casez(r_axil_addr) + UART_SETUP: axil_read_data <= { 1'b0, uart_setup }; + UART_FIFO: axil_read_data <= axil_fifo_data; + UART_RXREG: axil_read_data <= axil_rx_data; + UART_TXREG: axil_read_data <= axil_tx_data; + endcase + + if (OPT_LOWPOWER && !r_preread) + axil_read_data <= 0; + end + // }}} + + function [C_AXI_DATA_WIDTH-1:0] apply_wstrb; + input [C_AXI_DATA_WIDTH-1:0] prior_data; + input [C_AXI_DATA_WIDTH-1:0] new_data; + input [C_AXI_DATA_WIDTH/8-1:0] wstrb; + + integer k; + for(k=0; k<C_AXI_DATA_WIDTH/8; k=k+1) + begin + apply_wstrb[k*8 +: 8] + = wstrb[k] ? new_data[k*8 +: 8] : prior_data[k*8 +: 8]; + end + endfunction + // }}} + + //////////////////////////////////////////////////////////////////////// + // + // Veri1ator lint-check + // {{{ + // Verilator lint_off UNUSED + wire unused; + assign unused = &{ 1'b0, S_AXI_AWPROT, S_AXI_ARPROT, + S_AXI_ARADDR[ADDRLSB-1:0], + S_AXI_AWADDR[ADDRLSB-1:0], new_setup[31] }; + // Verilator lint_on UNUSED + // }}} +`ifdef FORMAL + //////////////////////////////////////////////////////////////////////// + // + // Formal properties used in verfiying this core + // + //////////////////////////////////////////////////////////////////////// + // + // {{{ + reg f_past_valid; + initial f_past_valid = 0; + always @(posedge S_AXI_ACLK) + f_past_valid <= 1; + + //////////////////////////////////////////////////////////////////////// + // + // The AXI-lite control interface + // + //////////////////////////////////////////////////////////////////////// + // + // {{{ + localparam F_AXIL_LGDEPTH = 4; + wire [F_AXIL_LGDEPTH-1:0] faxil_rd_outstanding, + faxil_wr_outstanding, + faxil_awr_outstanding; + + faxil_slave #( + // {{{ + .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH), + .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH), + .F_LGDEPTH(F_AXIL_LGDEPTH), + .F_AXI_MAXWAIT(4), + .F_AXI_MAXDELAY(4), + .F_AXI_MAXRSTALL(3), + .F_OPT_COVER_BURST(4) + // }}} + ) faxil( + // {{{ + .i_clk(S_AXI_ACLK), .i_axi_reset_n(S_AXI_ARESETN), + // + .i_axi_awvalid(S_AXI_AWVALID), + .i_axi_awready(S_AXI_AWREADY), + .i_axi_awaddr( S_AXI_AWADDR), + .i_axi_awcache(4'h0), + .i_axi_awprot( S_AXI_AWPROT), + // + .i_axi_wvalid(S_AXI_WVALID), + .i_axi_wready(S_AXI_WREADY), + .i_axi_wdata( S_AXI_WDATA), + .i_axi_wstrb( S_AXI_WSTRB), + // + .i_axi_bvalid(S_AXI_BVALID), + .i_axi_bready(S_AXI_BREADY), + .i_axi_bresp( S_AXI_BRESP), + // + .i_axi_arvalid(S_AXI_ARVALID), + .i_axi_arready(S_AXI_ARREADY), + .i_axi_araddr( S_AXI_ARADDR), + .i_axi_arcache(4'h0), + .i_axi_arprot( S_AXI_ARPROT), + // + .i_axi_rvalid(S_AXI_RVALID), + .i_axi_rready(S_AXI_RREADY), + .i_axi_rdata( S_AXI_RDATA), + .i_axi_rresp( S_AXI_RRESP), + // + .f_axi_rd_outstanding(faxil_rd_outstanding), + .f_axi_wr_outstanding(faxil_wr_outstanding), + .f_axi_awr_outstanding(faxil_awr_outstanding) + // }}} + ); + + always @(*) + if (OPT_SKIDBUFFER) + begin + assert(faxil_awr_outstanding== (S_AXI_BVALID ? 1:0) + +(S_AXI_AWREADY ? 0:1)); + assert(faxil_wr_outstanding == (S_AXI_BVALID ? 1:0) + +(S_AXI_WREADY ? 0:1)); + + assert(faxil_rd_outstanding == (S_AXI_RVALID ? 1:0) + + (r_preread ? 1:0) +(S_AXI_ARREADY ? 0:1)); + end else begin + assert(faxil_wr_outstanding == (S_AXI_BVALID ? 1:0)); + assert(faxil_awr_outstanding == faxil_wr_outstanding); + + assert(faxil_rd_outstanding == (S_AXI_RVALID ? 1:0) + + (r_preread ? 1:0)); + + assert(S_AXI_ARREADY == (!S_AXI_RVALID && !r_preread)); + end + +`ifdef VERIFIC + assert property (@(posedge S_AXI_ACLK) + disable iff (!S_AXI_ARESETN || (S_AXI_RVALID && !S_AXI_RREADY)) + S_AXI_ARVALID && S_AXI_ARREADY && S_AXI_ARADDR[3:2]== UART_SETUP + |=> r_preread && r_axil_addr == UART_SETUP + ##1 S_AXI_RVALID && axil_read_data + == { 1'b0, $past(uart_setup) }); + + assert property (@(posedge S_AXI_ACLK) + disable iff (!S_AXI_ARESETN || (S_AXI_RVALID && !S_AXI_RREADY)) + S_AXI_ARVALID && S_AXI_ARREADY && S_AXI_ARADDR[3:2] == UART_FIFO + |=> r_preread && r_axil_addr == UART_FIFO + ##1 S_AXI_RVALID && axil_read_data == $past(axil_fifo_data)); + + assert property (@(posedge S_AXI_ACLK) + disable iff (!S_AXI_ARESETN || (S_AXI_RVALID && !S_AXI_RREADY)) + S_AXI_ARVALID && S_AXI_ARREADY && S_AXI_ARADDR[3:2]== UART_RXREG + |=> r_preread && r_axil_addr == UART_RXREG + ##1 S_AXI_RVALID && axil_read_data == $past(axil_rx_data)); + + assert property (@(posedge S_AXI_ACLK) + disable iff (!S_AXI_ARESETN || (S_AXI_RVALID && !S_AXI_RREADY)) + S_AXI_ARVALID && S_AXI_ARREADY && S_AXI_ARADDR[3:2]== UART_TXREG + |=> r_preread && r_axil_addr == UART_TXREG + ##1 S_AXI_RVALID && axil_read_data == $past(axil_tx_data)); + +`endif + // + // Check that our low-power only logic works by verifying that anytime + // S_AXI_RVALID is inactive, then the outgoing data is also zero. + // + always @(*) + if (OPT_LOWPOWER && !S_AXI_RVALID) + assert(S_AXI_RDATA == 0); + + // }}} + //////////////////////////////////////////////////////////////////////// + // + // Cover checks + // + //////////////////////////////////////////////////////////////////////// + // + // {{{ + + // While there are already cover properties in the formal property + // set above, you'll probably still want to cover something + // application specific here + + // }}} + // }}} +`endif +endmodule
diff --git a/verilog/rtl/wbuart32/rxuart.v b/verilog/rtl/wbuart32/rxuart.v new file mode 100644 index 0000000..43a2928 --- /dev/null +++ b/verilog/rtl/wbuart32/rxuart.v
@@ -0,0 +1,513 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Filename: rxuart.v +// {{{ +// Project: wbuart32, a full featured UART with simulator +// +// Purpose: Receive and decode inputs from a single UART line. +// +// +// To interface with this module, connect it to your system clock, +// pass it the 32 bit setup register (defined below) and the UART +// input. When data becomes available, the o_wr line will be asserted +// for one clock cycle. On parity or frame errors, the o_parity_err +// or o_frame_err lines will be asserted. Likewise, on a break +// condition, o_break will be asserted. These lines are self clearing. +// +// There is a synchronous reset line, logic high. +// +// Now for the setup register. The register is 32 bits, so that this +// UART may be set up over a 32-bit bus. +// +// i_setup[30] True if we are not using hardware flow control. This bit +// is ignored within this module, as any receive hardware flow +// control will need to be implemented elsewhere. +// +// i_setup[29:28] Indicates the number of data bits per word. This will +// either be 2'b00 for an 8-bit word, 2'b01 for a 7-bit word, 2'b10 +// for a six bit word, or 2'b11 for a five bit word. +// +// i_setup[27] Indicates whether or not to use one or two stop bits. +// Set this to one to expect two stop bits, zero for one. +// +// i_setup[26] Indicates whether or not a parity bit exists. Set this +// to 1'b1 to include parity. +// +// i_setup[25] Indicates whether or not the parity bit is fixed. Set +// to 1'b1 to include a fixed bit of parity, 1'b0 to allow the +// parity to be set based upon data. (Both assume the parity +// enable value is set.) +// +// i_setup[24] This bit is ignored if parity is not used. Otherwise, +// in the case of a fixed parity bit, this bit indicates whether +// mark (1'b1) or space (1'b0) parity is used. Likewise if the +// parity is not fixed, a 1'b1 selects even parity, and 1'b0 +// selects odd. +// +// i_setup[23:0] Indicates the speed of the UART in terms of clocks. +// So, for example, if you have a 200 MHz clock and wish to +// run your UART at 9600 baud, you would take 200 MHz and divide +// by 9600 to set this value to 24'd20834. Likewise if you wished +// to run this serial port at 115200 baud from a 200 MHz clock, +// you would set the value to 24'd1736 +// +// Thus, to set the UART for the common setting of an 8-bit word, +// one stop bit, no parity, and 115200 baud over a 200 MHz clock, you +// would want to set the setup value to: +// +// 32'h0006c8 // For 115,200 baud, 8 bit, no parity +// 32'h005161 // For 9600 baud, 8 bit, no parity +// +// +// +// Creator: Dan Gisselquist, Ph.D. +// Gisselquist Technology, LLC +// +//////////////////////////////////////////////////////////////////////////////// +// }}} +// Copyright (C) 2015-2021, Gisselquist Technology, LLC +// {{{ +// This program is free software (firmware): you can redistribute it and/or +// modify it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program. (It's in the $(ROOT)/doc directory. Run make with no +// target there if the PDF file isn't present.) If not, see +// <http://www.gnu.org/licenses/> for a copy. +// +// License: GPL, v3, as defined and found on www.gnu.org, +// http://www.gnu.org/licenses/gpl.html +// +// +//////////////////////////////////////////////////////////////////////////////// +// +// +//`default_nettype none +// }}} +module rxuart #( + // {{{ + // 8 data bits, no parity, (at least 1) stop bit + parameter [30:0] INITIAL_SETUP = 31'd868 + // States: (@ baud counter == 0) + // 0 First bit arrives + // ..7 Bits arrive + // 8 Stop bit (x1) + // 9 Stop bit (x2) + // c break condition + // d Waiting for the channel to go high + // e Waiting for the reset to complete + // f Idle state + // }}} + ) ( + // {{{ + input wire i_clk, i_reset, + /* verilator lint_off UNUSED */ + input wire [30:0] i_setup, + /* verilator lint_on UNUSED */ + input wire i_uart_rx, + output reg o_wr, + output reg [7:0] o_data, + output reg o_break, + output reg o_parity_err, o_frame_err, + output wire o_ck_uart + // }}} + ); + localparam [3:0] RXU_BIT_ZERO = 4'h0; + localparam [3:0] RXU_BIT_ONE = 4'h1; + localparam [3:0] RXU_BIT_TWO = 4'h2; + localparam [3:0] RXU_BIT_THREE = 4'h3; + //localparam [3:0] RXU_BIT_FOUR = 4'h4, // UNUSED + //localparam [3:0] RXU_BIT_FIVE = 4'h5, // UNUSED + //localparam [3:0] RXU_BIT_SIX = 4'h6, // UNUSED + localparam [3:0] RXU_BIT_SEVEN = 4'h7; + localparam [3:0] RXU_PARITY = 4'h8; + localparam [3:0] RXU_STOP = 4'h9; + localparam [3:0] RXU_SECOND_STOP = 4'ha; + //localparam [3:0] Unused 4'hb + //localparam [3:0] Unused 4'hc + localparam [3:0] RXU_BREAK = 4'hd; + localparam [3:0] RXU_RESET_IDLE = 4'he; + localparam [3:0] RXU_IDLE = 4'hf; + // Signal declarations + // {{{ + wire [27:0] clocks_per_baud, break_condition, half_baud; + wire [1:0] data_bits; + wire use_parity, parity_even, dblstop, fixd_parity; + reg [29:0] r_setup; + reg [3:0] state; + + reg [27:0] baud_counter; + reg zero_baud_counter; + reg q_uart, qq_uart, ck_uart; + reg [27:0] chg_counter; + reg line_synch; + reg half_baud_time; + reg [7:0] data_reg; + reg calc_parity; + reg pre_wr; + + assign clocks_per_baud = { 4'h0, r_setup[23:0] }; + // assign hw_flow_control = !r_setup[30]; + assign data_bits = r_setup[29:28]; + assign dblstop = r_setup[27]; + assign use_parity = r_setup[26]; + assign fixd_parity = r_setup[25]; + assign parity_even = r_setup[24]; + assign break_condition = { r_setup[23:0], 4'h0 }; + assign half_baud = { 5'h00, r_setup[23:1] }-28'h1; + + // }}} + + // ck_uart + // {{{ + // Since this is an asynchronous receiver, we need to register our + // input a couple of clocks over to avoid any problems with + // metastability. We do that here, and then ignore all but the + // ck_uart wire. + initial q_uart = 1'b0; + initial qq_uart = 1'b0; + initial ck_uart = 1'b0; + always @(posedge i_clk) + begin + q_uart <= i_uart_rx; + qq_uart <= q_uart; + ck_uart <= qq_uart; + end + // }}} + + // o_ck_uart + // {{{ + // In case anyone else wants this clocked, stabilized value, we + // offer it on our output. + assign o_ck_uart = ck_uart; + // }}} + + // chg_counter + // {{{ + // Keep track of the number of clocks since the last change. + // + // This is used to determine if we are in either a break or an idle + // condition, as discussed further below. + initial chg_counter = 28'h00; + always @(posedge i_clk) + if (i_reset) + chg_counter <= 28'h00; + else if (qq_uart != ck_uart) + chg_counter <= 28'h00; + else if (chg_counter < break_condition) + chg_counter <= chg_counter + 1; + // }}} + + // o_break + // {{{ + // Are we in a break condition? + // + // A break condition exists if the line is held low for longer than + // a data word. Hence, we keep track of when the last change occurred. + // If it was more than break_condition clocks ago, and the current input + // value is a 0, then we're in a break--and nothing can be read until + // the line idles again. + initial o_break = 1'b0; + always @(posedge i_clk) + o_break <= ((chg_counter >= break_condition)&&(~ck_uart))? 1'b1:1'b0; + // }}} + + // line_synch + // {{{ + // Are we between characters? + // + // The opposite of a break condition is where the line is held high + // for more clocks than would be in a character. When this happens, + // we know we have synchronization--otherwise, we might be sampling + // from within a data word. + // + // This logic is used later to hold the RXUART in a reset condition + // until we know we are between data words. At that point, we should + // be able to hold on to our synchronization. + initial line_synch = 1'b0; + always @(posedge i_clk) + line_synch <= ((chg_counter >= break_condition)&&(ck_uart)); + // }}} + + // half_baud_time + // {{{ + // Are we in the middle of a baud iterval? Specifically, are we + // in the middle of a start bit? Set this to high if so. We'll use + // this within our state machine to transition out of the IDLE + // state. + initial half_baud_time = 0; + always @(posedge i_clk) + half_baud_time <= (~ck_uart)&&(chg_counter >= half_baud); + // }}} + + // r_setup + // {{{ + // Allow our controlling processor to change our setup at any time + // outside of receiving/processing a character. + initial r_setup = INITIAL_SETUP[29:0]; + always @(posedge i_clk) + if (state >= RXU_RESET_IDLE) + r_setup <= i_setup[29:0]; + // }}} + + // state -- the monster state machine + // {{{ + // Our monster state machine. YIKES! + // + // Yeah, this may be more complicated than it needs to be. The basic + // progression is: + // RESET -> RESET_IDLE -> (when line is idle) -> IDLE + // IDLE -> bit 0 -> bit 1 -> bit_{ndatabits} -> + // (optional) PARITY -> STOP -> (optional) SECOND_STOP + // -> IDLE + // ANY -> (on break) BREAK -> IDLE + // + // There are 16 states, although all are not used. These are listed + // at the top of this file. + // + // Logic inputs (12): (I've tried to minimize this number) + // state (4) + // i_reset + // line_synch + // o_break + // ckuart + // half_baud_time + // zero_baud_counter + // use_parity + // dblstop + // Logic outputs (4): + // state + // + initial state = RXU_RESET_IDLE; + always @(posedge i_clk) + if (i_reset) + state <= RXU_RESET_IDLE; + else if (state == RXU_RESET_IDLE) + begin + // {{{ + if (line_synch) + // Goto idle state from a reset + state <= RXU_IDLE; + else // Otherwise, stay in this condition 'til reset + state <= RXU_RESET_IDLE; + // }}} + end else if (o_break) + begin // We are in a break condition + state <= RXU_BREAK; + end else if (state == RXU_BREAK) + begin // Goto idle state following return ck_uart going high + // {{{ + if (ck_uart) + state <= RXU_IDLE; + else + state <= RXU_BREAK; + // }}} + end else if (state == RXU_IDLE) + begin // Idle state, independent of baud counter + // {{{ + if ((~ck_uart)&&(half_baud_time)) + begin + // We are in the center of a valid start bit + case (data_bits) + 2'b00: state <= RXU_BIT_ZERO; + 2'b01: state <= RXU_BIT_ONE; + 2'b10: state <= RXU_BIT_TWO; + 2'b11: state <= RXU_BIT_THREE; + endcase + end else // Otherwise, just stay here in idle + state <= RXU_IDLE; + // }}} + end else if (zero_baud_counter) + begin + // {{{ + if (state < RXU_BIT_SEVEN) + // Data arrives least significant bit first. + // By the time this is clocked in, it's what + // you'll have. + state <= state + 1; + else if (state == RXU_BIT_SEVEN) + state <= (use_parity) ? RXU_PARITY:RXU_STOP; + else if (state == RXU_PARITY) + state <= RXU_STOP; + else if (state == RXU_STOP) + begin // Stop (or parity) bit(s) + if (~ck_uart) // On frame error, wait 4 ch idle + state <= RXU_RESET_IDLE; + else if (dblstop) + state <= RXU_SECOND_STOP; + else + state <= RXU_IDLE; + end else // state must equal RX_SECOND_STOP + begin + if (~ck_uart) // On frame error, wait 4 ch idle + state <= RXU_RESET_IDLE; + else + state <= RXU_IDLE; + end + // }}} + end + // }}} + + // data_reg -- Data bit capture logic. + // {{{ + // This is drastically simplified from the state machine above, based + // upon: 1) it doesn't matter what it is until the end of a captured + // byte, and 2) the data register will flush itself of any invalid + // data in all other cases. Hence, let's keep it real simple. + // The only trick, though, is that if we have parity, then the data + // register needs to be held through that state without getting + // updated. + always @(posedge i_clk) + if ((zero_baud_counter)&&(state != RXU_PARITY)) + data_reg <= { ck_uart, data_reg[7:1] }; + // }}} + + // calc_parity + // {{{ + // Parity calculation logic + // + // As with the data capture logic, all that must be known about this + // bit is that it is the exclusive-OR of all bits prior. The first + // of those will follow idle, so we set ourselves to zero on idle. + // Then, as we walk through the states of a bit, all will adjust this + // value up until the parity bit, where the value will be read. Setting + // it then or after will be irrelevant, so ... this should be good + // and simplified. Note--we don't need to adjust this on reset either, + // since the reset state will lead to the idle state where we'll be + // reset before any transmission takes place. + always @(posedge i_clk) + if (state == RXU_IDLE) + calc_parity <= 0; + else if (zero_baud_counter) + calc_parity <= calc_parity ^ ck_uart; + // }}} + + // o_parity_err -- Parity error logic + // {{{ + // Set during the parity bit interval, read during the last stop bit + // interval, cleared on BREAK, RESET_IDLE, or IDLE states. + initial o_parity_err = 1'b0; + always @(posedge i_clk) + if ((zero_baud_counter)&&(state == RXU_PARITY)) + begin + if (fixd_parity) + // Fixed parity bit--independent of any dat + // value. + o_parity_err <= (ck_uart ^ parity_even); + else if (parity_even) + // Parity even: The XOR of all bits including + // the parity bit must be zero. + o_parity_err <= (calc_parity != ck_uart); + else + // Parity odd: the parity bit must equal the + // XOR of all the data bits. + o_parity_err <= (calc_parity == ck_uart); + end else if (state >= RXU_BREAK) + o_parity_err <= 1'b0; + // }}} + + // o_frame_err -- Frame error determination + // {{{ + // For the purpose of this controller, a frame error is defined as a + // stop bit (or second stop bit, if so enabled) not being high midway + // through the stop baud interval. The frame error value is + // immediately read, so we can clear it under all other circumstances. + // Specifically, we want it clear in RXU_BREAK, RXU_RESET_IDLE, and + // most importantly in RXU_IDLE. + initial o_frame_err = 1'b0; + always @(posedge i_clk) + if ((zero_baud_counter)&&((state == RXU_STOP) + ||(state == RXU_SECOND_STOP))) + o_frame_err <= (o_frame_err)||(~ck_uart); + else if ((zero_baud_counter)||(state >= RXU_BREAK)) + o_frame_err <= 1'b0; + // }}} + + // pre_wr, o_data + // {{{ + // Our data bit logic doesn't need nearly the complexity of all that + // work above. Indeed, we only need to know if we are at the end of + // a stop bit, in which case we copy the data_reg into our output + // data register, o_data. + // + // We would also set o_wr to be true when this is the case, but ... we + // won't know if there is a frame error on the second stop bit for + // another baud interval yet. So, instead, we set up the logic so that + // we know on the next zero baud counter that we can write out. That's + // the purpose of pre_wr. + initial o_data = 8'h00; + initial pre_wr = 1'b0; + always @(posedge i_clk) + if (i_reset) + begin + pre_wr <= 1'b0; + o_data <= 8'h00; + end else if ((zero_baud_counter)&&(state == RXU_STOP)) + begin + pre_wr <= 1'b1; + case (data_bits) + 2'b00: o_data <= data_reg; + 2'b01: o_data <= { 1'b0, data_reg[7:1] }; + 2'b10: o_data <= { 2'b0, data_reg[7:2] }; + 2'b11: o_data <= { 3'b0, data_reg[7:3] }; + endcase + end else if ((zero_baud_counter)||(state == RXU_IDLE)) + pre_wr <= 1'b0; + // }}} + + // o_wr + // {{{ + // Create an output strobe, true for one clock only, once we know + // all we need to know. o_data will be set on the last baud interval, + // o_parity_err on the last parity baud interval (if it existed, + // cleared otherwise, so ... we should be good to go here.) + initial o_wr = 1'b0; + always @(posedge i_clk) + if ((zero_baud_counter)||(state == RXU_IDLE)) + o_wr <= (pre_wr)&&(!i_reset); + else + o_wr <= 1'b0; + // }}} + + // The baud counter + // {{{ + // This is used as a "clock divider" if you will, but the clock needs + // to be reset before any byte can be decoded. In all other respects, + // we set ourselves up for clocks_per_baud counts between baud + // intervals. + always @(posedge i_clk) + if (i_reset) + baud_counter <= clocks_per_baud-28'h01; + else if (zero_baud_counter) + baud_counter <= clocks_per_baud-28'h01; + else case(state) + RXU_RESET_IDLE:baud_counter <= clocks_per_baud-28'h01; + RXU_BREAK: baud_counter <= clocks_per_baud-28'h01; + RXU_IDLE: baud_counter <= clocks_per_baud-28'h01; + default: baud_counter <= baud_counter-28'h01; + endcase + // }}} + + // zero_baud_counter + // {{{ + // Rather than testing whether or not (baud_counter == 0) within our + // (already too complicated) state transition tables, we use + // zero_baud_counter to pre-charge that test on the clock + // before--cleaning up some otherwise difficult timing dependencies. + initial zero_baud_counter = 1'b0; + always @(posedge i_clk) + if (state == RXU_IDLE) + zero_baud_counter <= 1'b0; + else + zero_baud_counter <= (baud_counter == 28'h01); + // }}} +endmodule + +
diff --git a/verilog/rtl/wbuart32/rxuartlite.v b/verilog/rtl/wbuart32/rxuartlite.v new file mode 100644 index 0000000..988b664 --- /dev/null +++ b/verilog/rtl/wbuart32/rxuartlite.v
@@ -0,0 +1,755 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Filename: rxuartlite.v +// {{{ +// Project: wbuart32, a full featured UART with simulator +// +// Purpose: Receive and decode inputs from a single UART line. +// +// +// To interface with this module, connect it to your system clock, +// and a UART input. Set the parameter to the number of clocks per +// baud. When data becomes available, the o_wr line will be asserted +// for one clock cycle. +// +// This interface only handles 8N1 serial port communications. It does +// not handle the break, parity, or frame error conditions. +// +// +// Creator: Dan Gisselquist, Ph.D. +// Gisselquist Technology, LLC +// +//////////////////////////////////////////////////////////////////////////////// +// }}} +// Copyright (C) 2015-2021, Gisselquist Technology, LLC +// {{{ +// This program is free software (firmware): you can redistribute it and/or +// modify it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program. (It's in the $(ROOT)/doc directory. Run make with no +// target there if the PDF file isn't present.) If not, see +// <http://www.gnu.org/licenses/> for a copy. +// }}} +// License: GPL, v3, as defined and found on www.gnu.org, +// {{{ +// http://www.gnu.org/licenses/gpl.html +// +// +//////////////////////////////////////////////////////////////////////////////// +// +// +//`default_nettype none +// }}} +module rxuartlite #( + // {{{ + parameter TIMER_BITS = 10, +`ifdef FORMAL + parameter [(TIMER_BITS-1):0] CLOCKS_PER_BAUD = 16, // Necessary for formal proof +`else + parameter [(TIMER_BITS-1):0] CLOCKS_PER_BAUD = 868 // 115200 MBaud at 100MHz +`endif + + // }}} + ) ( + // {{{ + input wire i_clk, + input wire i_uart_rx, + output reg o_wr, + output reg [7:0] o_data + // }}} + ); + + + localparam TB = TIMER_BITS; + // + localparam [3:0] RXUL_BIT_ZERO = 4'h0; + // Verilator lint_off UNUSED + // These are used by the formal solver + localparam [3:0] RXUL_BIT_ONE = 4'h1; + localparam [3:0] RXUL_BIT_TWO = 4'h2; + localparam [3:0] RXUL_BIT_THREE = 4'h3; + localparam [3:0] RXUL_BIT_FOUR = 4'h4; + localparam [3:0] RXUL_BIT_FIVE = 4'h5; + localparam [3:0] RXUL_BIT_SIX = 4'h6; + localparam [3:0] RXUL_BIT_SEVEN = 4'h7; + // Verilator lint_on UNUSED + localparam [3:0] RXUL_STOP = 4'h8; + localparam [3:0] RXUL_WAIT = 4'h9; + localparam [3:0] RXUL_IDLE = 4'hf; + + // Signal/register declarations + // {{{ + wire [(TB-1):0] half_baud; + reg [3:0] state; + + assign half_baud = { 1'b0, CLOCKS_PER_BAUD[(TB-1):1] }; + reg [(TB-1):0] baud_counter; + reg zero_baud_counter; + + reg q_uart, qq_uart, ck_uart; + reg [(TB-1):0] chg_counter; + reg half_baud_time; + reg [7:0] data_reg; + // }}} + + // ck_uart + // {{{ + // Since this is an asynchronous receiver, we need to register our + // input a couple of clocks over to avoid any problems with + // metastability. We do that here, and then ignore all but the + // ck_uart wire. + initial q_uart = 1'b1; + initial qq_uart = 1'b1; + initial ck_uart = 1'b1; + always @(posedge i_clk) + { ck_uart, qq_uart, q_uart } <= { qq_uart, q_uart, i_uart_rx }; + // }}} + + // chg_counter + // {{{ + // Keep track of the number of clocks since the last change. + // + // This is used to determine if we are in either a break or an idle + // condition, as discussed further below. + initial chg_counter = {(TB){1'b1}}; + always @(posedge i_clk) + if (qq_uart != ck_uart) + chg_counter <= 0; + else if (chg_counter != { (TB){1'b1} }) + chg_counter <= chg_counter + 1; + // }}} + + // half_baud_time + // {{{ + // Are we in the middle of a baud iterval? Specifically, are we + // in the middle of a start bit? Set this to high if so. We'll use + // this within our state machine to transition out of the IDLE + // state. + initial half_baud_time = 0; + always @(posedge i_clk) + half_baud_time <= (!ck_uart)&&(chg_counter >= half_baud-1'b1-1'b1); + // }}} + + // state + // {{{ + initial state = RXUL_IDLE; + always @(posedge i_clk) + if (state == RXUL_IDLE) + begin // Idle state, independent of baud counter + // {{{ + // By default, just stay in the IDLE state + state <= RXUL_IDLE; + if ((!ck_uart)&&(half_baud_time)) + // UNLESS: We are in the center of a valid + // start bit + state <= RXUL_BIT_ZERO; + // }}} + end else if ((state >= RXUL_WAIT)&&(ck_uart)) + state <= RXUL_IDLE; + else if (zero_baud_counter) + begin + // {{{ + if (state <= RXUL_STOP) + // Data arrives least significant bit first. + // By the time this is clocked in, it's what + // you'll have. + state <= state + 1; + // }}} + end + // }}} + + // data_reg + // {{{ + // Data bit capture logic. + // + // This is drastically simplified from the state machine above, based + // upon: 1) it doesn't matter what it is until the end of a captured + // byte, and 2) the data register will flush itself of any invalid + // data in all other cases. Hence, let's keep it real simple. + always @(posedge i_clk) + if ((zero_baud_counter)&&(state != RXUL_STOP)) + data_reg <= { qq_uart, data_reg[7:1] }; + // }}} + + // o_wr, o_data + // {{{ + // Our data bit logic doesn't need nearly the complexity of all that + // work above. Indeed, we only need to know if we are at the end of + // a stop bit, in which case we copy the data_reg into our output + // data register, o_data, and tell others (for one clock) that data is + // available. + // + initial o_wr = 1'b0; + initial o_data = 8'h00; + always @(posedge i_clk) + if ((zero_baud_counter)&&(state == RXUL_STOP)&&(ck_uart)) + begin + o_wr <= 1'b1; + o_data <= data_reg; + end else + o_wr <= 1'b0; + // }}} + + // baud_counter -- The baud counter + // {{{ + // This is used as a "clock divider" if you will, but the clock needs + // to be reset before any byte can be decoded. In all other respects, + // we set ourselves up for CLOCKS_PER_BAUD counts between baud + // intervals. + initial baud_counter = 0; + always @(posedge i_clk) + if (((state==RXUL_IDLE))&&(!ck_uart)&&(half_baud_time)) + baud_counter <= CLOCKS_PER_BAUD-1'b1; + else if (state == RXUL_WAIT) + baud_counter <= 0; + else if ((zero_baud_counter)&&(state < RXUL_STOP)) + baud_counter <= CLOCKS_PER_BAUD-1'b1; + else if (!zero_baud_counter) + baud_counter <= baud_counter-1'b1; + // }}} + + // zero_baud_counter + // {{{ + // Rather than testing whether or not (baud_counter == 0) within our + // (already too complicated) state transition tables, we use + // zero_baud_counter to pre-charge that test on the clock + // before--cleaning up some otherwise difficult timing dependencies. + initial zero_baud_counter = 1'b1; + always @(posedge i_clk) + if ((state == RXUL_IDLE)&&(!ck_uart)&&(half_baud_time)) + zero_baud_counter <= 1'b0; + else if (state == RXUL_WAIT) + zero_baud_counter <= 1'b1; + else if ((zero_baud_counter)&&(state < RXUL_STOP)) + zero_baud_counter <= 1'b0; + else if (baud_counter == 1) + zero_baud_counter <= 1'b1; + // }}} +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// +// Formal properties +// {{{ +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + // Declarations + // {{{ +`ifdef FORMAL +`define FORMAL_VERILATOR +`else +`ifdef VERILATOR +`define FORMAL_VERILATOR +`endif +`endif + +`ifdef FORMAL +`define ASSUME assume +`define ASSERT assert +`ifdef VERIFIC + // We need this to use $global_clock below + (* gclk *) wire gbl_clk; + global clocking @(posedge gbl_clk); endclocking +`endif + + + localparam F_CKRES = 10; + + (* anyseq *) wire f_tx_start; + (* anyconst *) wire [(F_CKRES-1):0] f_tx_step; + reg f_tx_zclk; + reg [(TB-1):0] f_tx_timer; + wire [7:0] f_rx_newdata; + reg [(TB-1):0] f_tx_baud; + wire f_tx_zbaud; + + wire [(TB-1):0] f_max_baud_difference; + reg [(TB-1):0] f_baud_difference; + reg [(TB+3):0] f_tx_count, f_rx_count; + (* anyseq *) wire [7:0] f_tx_data; + + wire f_txclk; + reg [1:0] f_rx_clock; + reg [(F_CKRES-1):0] f_tx_clock; + reg f_past_valid, f_past_valid_tx; + + reg [9:0] f_tx_reg; + reg f_tx_busy; + + // }}} + + initial f_past_valid = 1'b0; + always @(posedge i_clk) + f_past_valid <= 1'b1; + + initial f_rx_clock = 3'h0; + always @($global_clock) + f_rx_clock <= f_rx_clock + 1'b1; + + always @(*) + assume(i_clk == f_rx_clock[1]); + + + //////////////////////////////////////////////////////////////////////// + // + // Assume a transmitted signal + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + + // First, calculate the transmit clock + localparam [(F_CKRES-1):0] F_MIDSTEP = { 2'b01, {(F_CKRES-2){1'b0}} }; + // + // Need to allow us to slip by half a baud clock over 10 baud intervals + // + // (F_STEP / (2^F_CKRES)) * (CLOCKS_PER_BAUD)*10 < CLOCKS_PER_BAUD/2 + // F_STEP * 2 * 10 < 2^F_CKRES + localparam [(F_CKRES-1):0] F_HALFSTEP= F_MIDSTEP/32; + localparam [(F_CKRES-1):0] F_MINSTEP = F_MIDSTEP - F_HALFSTEP + 1; + localparam [(F_CKRES-1):0] F_MAXSTEP = F_MIDSTEP + F_HALFSTEP - 1; + + initial assert(F_MINSTEP <= F_MIDSTEP); + initial assert(F_MIDSTEP <= F_MAXSTEP); + + // assume((f_tx_step >= F_MINSTEP)&&(f_tx_step <= F_MAXSTEP)); + // + // + always @(*) assume((f_tx_step == F_MINSTEP) + ||(f_tx_step == F_MIDSTEP) + ||(f_tx_step == F_MAXSTEP)); + + always @($global_clock) + f_tx_clock <= f_tx_clock + f_tx_step; + + assign f_txclk = f_tx_clock[F_CKRES-1]; + // + initial f_past_valid_tx = 1'b0; + always @(posedge f_txclk) + f_past_valid_tx <= 1'b1; + + initial assume(i_uart_rx); + + //////////////////////////////////////////////////////////////////////// + // + // The simulated timing generator + + always @(*) + if (f_tx_busy) + assume(!f_tx_start); + + initial f_tx_baud = 0; + always @(posedge f_txclk) + if ((f_tx_zbaud)&&((f_tx_busy)||(f_tx_start))) + f_tx_baud <= CLOCKS_PER_BAUD-1'b1; + else if (!f_tx_zbaud) + f_tx_baud <= f_tx_baud - 1'b1; + + always @(*) + `ASSERT(f_tx_baud < CLOCKS_PER_BAUD); + + always @(*) + if (!f_tx_busy) + `ASSERT(f_tx_baud == 0); + + assign f_tx_zbaud = (f_tx_baud == 0); + + // But only if we aren't busy + initial assume(f_tx_data == 0); + always @(posedge f_txclk) + if ((!f_tx_zbaud)||(f_tx_busy)||(!f_tx_start)) + assume(f_tx_data == $past(f_tx_data)); + + // Force the data to change on a clock only + always @($global_clock) + if ((f_past_valid)&&(!$rose(f_txclk))) + assume($stable(f_tx_data)); + else if (f_tx_busy) + assume($stable(f_tx_data)); + + // + always @($global_clock) + if ((!f_past_valid)||(!$rose(f_txclk))) + begin + assume($stable(f_tx_start)); + assume($stable(f_tx_data)); + end + + // + // + // + + // Here's the transmitter itself (roughly) + initial f_tx_busy = 1'b0; + initial f_tx_reg = 0; + always @(posedge f_txclk) + if (!f_tx_zbaud) + begin + `ASSERT(f_tx_busy); + end else begin + f_tx_reg <= { 1'b0, f_tx_reg[9:1] }; + if (f_tx_start) + f_tx_reg <= { 1'b1, f_tx_data, 1'b0 }; + end + + // Create a busy flag that we'll use + always @(*) + if (!f_tx_zbaud) + f_tx_busy <= 1'b1; + else if (|f_tx_reg) + f_tx_busy <= 1'b1; + else + f_tx_busy <= 1'b0; + + // + // Tie the TX register to the TX data + always @(posedge f_txclk) + if (f_tx_reg[9]) + `ASSERT(f_tx_reg[8:0] == { f_tx_data, 1'b0 }); + else if (f_tx_reg[8]) + `ASSERT(f_tx_reg[7:0] == f_tx_data[7:0] ); + else if (f_tx_reg[7]) + `ASSERT(f_tx_reg[6:0] == f_tx_data[7:1] ); + else if (f_tx_reg[6]) + `ASSERT(f_tx_reg[5:0] == f_tx_data[7:2] ); + else if (f_tx_reg[5]) + `ASSERT(f_tx_reg[4:0] == f_tx_data[7:3] ); + else if (f_tx_reg[4]) + `ASSERT(f_tx_reg[3:0] == f_tx_data[7:4] ); + else if (f_tx_reg[3]) + `ASSERT(f_tx_reg[2:0] == f_tx_data[7:5] ); + else if (f_tx_reg[2]) + `ASSERT(f_tx_reg[1:0] == f_tx_data[7:6] ); + else if (f_tx_reg[1]) + `ASSERT(f_tx_reg[0] == f_tx_data[7]); + + // Our counter since we start + initial f_tx_count = 0; + always @(posedge f_txclk) + if (!f_tx_busy) + f_tx_count <= 0; + else + f_tx_count <= f_tx_count + 1'b1; + + always @(*) + if (f_tx_reg == 10'h0) + assume(i_uart_rx); + else + assume(i_uart_rx == f_tx_reg[0]); + + // + // Make sure the absolute transmit clock timer matches our state + // + always @(posedge f_txclk) + if (!f_tx_busy) + begin + if ((!f_past_valid_tx)||(!$past(f_tx_busy))) + `ASSERT(f_tx_count == 0); + end else if (f_tx_reg[9]) + `ASSERT(f_tx_count == + CLOCKS_PER_BAUD -1 -f_tx_baud); + else if (f_tx_reg[8]) + `ASSERT(f_tx_count == + 2 * CLOCKS_PER_BAUD -1 -f_tx_baud); + else if (f_tx_reg[7]) + `ASSERT(f_tx_count == + 3 * CLOCKS_PER_BAUD -1 -f_tx_baud); + else if (f_tx_reg[6]) + `ASSERT(f_tx_count == + 4 * CLOCKS_PER_BAUD -1 -f_tx_baud); + else if (f_tx_reg[5]) + `ASSERT(f_tx_count == + 5 * CLOCKS_PER_BAUD -1 -f_tx_baud); + else if (f_tx_reg[4]) + `ASSERT(f_tx_count == + 6 * CLOCKS_PER_BAUD -1 -f_tx_baud); + else if (f_tx_reg[3]) + `ASSERT(f_tx_count == + 7 * CLOCKS_PER_BAUD -1 -f_tx_baud); + else if (f_tx_reg[2]) + `ASSERT(f_tx_count == + 8 * CLOCKS_PER_BAUD -1 -f_tx_baud); + else if (f_tx_reg[1]) + `ASSERT(f_tx_count == + 9 * CLOCKS_PER_BAUD -1 -f_tx_baud); + else if (f_tx_reg[0]) + `ASSERT(f_tx_count == + 10 * CLOCKS_PER_BAUD -1 -f_tx_baud); + else + `ASSERT(f_tx_count == + 11 * CLOCKS_PER_BAUD -1 -f_tx_baud); + + // }}} + //////////////////////////////////////////////////////////////////////// + // + // Receiver + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + // Count RX clocks since the start of the first stop bit, measured in + // rx clocks + initial f_rx_count = 0; + always @(posedge i_clk) + if (state == RXUL_IDLE) + f_rx_count = (!ck_uart) ? (chg_counter+2) : 0; + else + f_rx_count <= f_rx_count + 1'b1; + always @(posedge i_clk) + if (state == 0) + `ASSERT(f_rx_count + == half_baud + (CLOCKS_PER_BAUD-baud_counter)); + else if (state == 1) + `ASSERT(f_rx_count == half_baud + 2 * CLOCKS_PER_BAUD + - baud_counter); + else if (state == 2) + `ASSERT(f_rx_count == half_baud + 3 * CLOCKS_PER_BAUD + - baud_counter); + else if (state == 3) + `ASSERT(f_rx_count == half_baud + 4 * CLOCKS_PER_BAUD + - baud_counter); + else if (state == 4) + `ASSERT(f_rx_count == half_baud + 5 * CLOCKS_PER_BAUD + - baud_counter); + else if (state == 5) + `ASSERT(f_rx_count == half_baud + 6 * CLOCKS_PER_BAUD + - baud_counter); + else if (state == 6) + `ASSERT(f_rx_count == half_baud + 7 * CLOCKS_PER_BAUD + - baud_counter); + else if (state == 7) + `ASSERT(f_rx_count == half_baud + 8 * CLOCKS_PER_BAUD + - baud_counter); + else if (state == 8) + `ASSERT((f_rx_count == half_baud + 9 * CLOCKS_PER_BAUD + - baud_counter) + ||(f_rx_count == half_baud + 10 * CLOCKS_PER_BAUD + - baud_counter)); + + always @(*) + `ASSERT( ((!zero_baud_counter) + &&(state == RXUL_IDLE) + &&(baud_counter == 0)) + ||((zero_baud_counter)&&(baud_counter == 0)) + ||((!zero_baud_counter)&&(baud_counter != 0))); + + always @(posedge i_clk) + if (!f_past_valid) + `ASSERT((state == RXUL_IDLE)&&(baud_counter == 0) + &&(zero_baud_counter)); + + always @(*) + begin + `ASSERT({ ck_uart,qq_uart,q_uart,i_uart_rx } != 4'h2); + `ASSERT({ ck_uart,qq_uart,q_uart,i_uart_rx } != 4'h4); + `ASSERT({ ck_uart,qq_uart,q_uart,i_uart_rx } != 4'h5); + `ASSERT({ ck_uart,qq_uart,q_uart,i_uart_rx } != 4'h6); + `ASSERT({ ck_uart,qq_uart,q_uart,i_uart_rx } != 4'h9); + `ASSERT({ ck_uart,qq_uart,q_uart,i_uart_rx } != 4'ha); + `ASSERT({ ck_uart,qq_uart,q_uart,i_uart_rx } != 4'hb); + `ASSERT({ ck_uart,qq_uart,q_uart,i_uart_rx } != 4'hd); + end + + always @(posedge i_clk) + if ((f_past_valid)&&($past(state) >= RXUL_WAIT)&&($past(ck_uart))) + `ASSERT(state == RXUL_IDLE); + + always @(posedge i_clk) + if ((f_past_valid)&&($past(state) >= RXUL_WAIT) + &&(($past(state) != RXUL_IDLE)||(state == RXUL_IDLE))) + `ASSERT(zero_baud_counter); + + // Calculate an absolute value of the difference between the two baud + // clocks + always @(posedge i_clk) + if ((f_past_valid)&&($past(state)==RXUL_IDLE)&&(state == RXUL_IDLE)) + begin + `ASSERT(($past(ck_uart)) + ||(chg_counter <= + { 1'b0, CLOCKS_PER_BAUD[(TB-1):1] })); + end + + always @(posedge f_txclk) + if (!f_past_valid_tx) + `ASSERT((state == RXUL_IDLE)&&(baud_counter == 0) + &&(zero_baud_counter)&&(!f_tx_busy)); + + wire [(TB+3):0] f_tx_count_two_clocks_ago; + assign f_tx_count_two_clocks_ago = f_tx_count - 2; + always @(*) + if (f_tx_count >= f_rx_count + 2) + f_baud_difference = f_tx_count_two_clocks_ago - f_rx_count; + else + f_baud_difference = f_rx_count - f_tx_count_two_clocks_ago; + + localparam F_SYNC_DLY = 8; + + reg [(TB+4+F_CKRES-1):0] f_sub_baud_difference; + reg [F_CKRES-1:0] ck_tx_clock; + reg [((F_SYNC_DLY-1)*F_CKRES)-1:0] q_tx_clock; + reg [TB+3:0] ck_tx_count; + reg [(F_SYNC_DLY-1)*(TB+4)-1:0] q_tx_count; + initial q_tx_count = 0; + initial ck_tx_count = 0; + initial q_tx_clock = 0; + initial ck_tx_clock = 0; + always @($global_clock) + { ck_tx_clock, q_tx_clock } <= { q_tx_clock, f_tx_clock }; + always @($global_clock) + { ck_tx_count, q_tx_count } <= { q_tx_count, f_tx_count }; + + + reg [TB+4+F_CKRES-1:0] f_ck_tx_time, f_rx_time; + always @(*) + f_ck_tx_time = { ck_tx_count, !ck_tx_clock[F_CKRES-1], + ck_tx_clock[F_CKRES-2:0] }; + always @(*) + f_rx_time = { f_rx_count, !f_rx_clock[1], f_rx_clock[0], + {(F_CKRES-2){1'b0}} }; + + reg [TB+4+F_CKRES-1:0] f_signed_difference; + always @(*) + f_signed_difference = f_ck_tx_time - f_rx_time; + + always @(*) + if (f_signed_difference[TB+4+F_CKRES-1]) + f_sub_baud_difference = -f_signed_difference; + else + f_sub_baud_difference = f_signed_difference; + + always @($global_clock) + if (state == RXUL_WAIT) + `ASSERT((!f_tx_busy)||(f_tx_reg[9:1] == 0)); + + always @($global_clock) + if (state == RXUL_IDLE) + begin + `ASSERT((!f_tx_busy)||(f_tx_reg[9])||(f_tx_reg[9:1]==0)); + if (!ck_uart) + ;//`PHASE_TWO_ASSERT((f_rx_count < 4)||(f_sub_baud_difference <= ((CLOCKS_PER_BAUD<<F_CKRES)/20))); + else + `ASSERT((f_tx_reg[9:1]==0)||(f_tx_count < (3 + CLOCKS_PER_BAUD/2))); + end else if (state == 0) + `ASSERT(f_sub_baud_difference + <= 2 * ((CLOCKS_PER_BAUD<<F_CKRES)/20)); + else if (state == 1) + `ASSERT(f_sub_baud_difference + <= 3 * ((CLOCKS_PER_BAUD<<F_CKRES)/20)); + else if (state == 2) + `ASSERT(f_sub_baud_difference + <= 4 * ((CLOCKS_PER_BAUD<<F_CKRES)/20)); + else if (state == 3) + `ASSERT(f_sub_baud_difference + <= 5 * ((CLOCKS_PER_BAUD<<F_CKRES)/20)); + else if (state == 4) + `ASSERT(f_sub_baud_difference + <= 6 * ((CLOCKS_PER_BAUD<<F_CKRES)/20)); + else if (state == 5) + `ASSERT(f_sub_baud_difference + <= 7 * ((CLOCKS_PER_BAUD<<F_CKRES)/20)); + else if (state == 6) + `ASSERT(f_sub_baud_difference + <= 8 * ((CLOCKS_PER_BAUD<<F_CKRES)/20)); + else if (state == 7) + `ASSERT(f_sub_baud_difference + <= 9 * ((CLOCKS_PER_BAUD<<F_CKRES)/20)); + else if (state == 8) + `ASSERT(f_sub_baud_difference + <= 10 * ((CLOCKS_PER_BAUD<<F_CKRES)/20)); + + always @(posedge i_clk) + if (o_wr) + `ASSERT(o_data == $past(f_tx_data,4)); + + // always @(posedge i_clk) + // if ((zero_baud_counter)&&(state != 4'hf)&&(CLOCKS_PER_BAUD > 6)) + // assert(i_uart_rx == ck_uart); + + // Make sure the data register matches + always @(posedge i_clk) + // if ((f_past_valid)&&(state != $past(state))) + begin + if (state == 4'h0) + `ASSERT(!data_reg[7]); + + if (state == 4'h1) + `ASSERT((data_reg[7] + == $past(f_tx_data[0]))&&(!data_reg[6])); + + if (state == 4'h2) + `ASSERT(data_reg[7:6] + == $past(f_tx_data[1:0])); + + if (state == 4'h3) + `ASSERT(data_reg[7:5] == $past(f_tx_data[2:0])); + + if (state == 4'h4) + `ASSERT(data_reg[7:4] == $past(f_tx_data[3:0])); + + if (state == 4'h5) + `ASSERT(data_reg[7:3] == $past(f_tx_data[4:0])); + + if (state == 4'h6) + `ASSERT(data_reg[7:2] == $past(f_tx_data[5:0])); + + if (state == 4'h7) + `ASSERT(data_reg[7:1] == $past(f_tx_data[6:0])); + + if (state == 4'h8) + `ASSERT(data_reg[7:0] == $past(f_tx_data[7:0])); + end + // }}} + //////////////////////////////////////////////////////////////////////// + // + // Cover properties + // {{{{ + //////////////////////////////////////////////////////////////////////// + // + always @(posedge i_clk) + cover(o_wr); // Step 626, takes about 20mins + + always @(posedge i_clk) + begin + cover(!ck_uart); + cover((f_past_valid)&&($rose(ck_uart))); // 82 + cover((zero_baud_counter)&&(state == RXUL_BIT_ZERO)); // 110 + cover((zero_baud_counter)&&(state == RXUL_BIT_ONE)); // 174 + cover((zero_baud_counter)&&(state == RXUL_BIT_TWO)); // 238 + cover((zero_baud_counter)&&(state == RXUL_BIT_THREE));// 302 + cover((zero_baud_counter)&&(state == RXUL_BIT_FOUR)); // 366 + cover((zero_baud_counter)&&(state == RXUL_BIT_FIVE)); // 430 + cover((zero_baud_counter)&&(state == RXUL_BIT_SIX)); // 494 + cover((zero_baud_counter)&&(state == RXUL_BIT_SEVEN));// 558 + cover((zero_baud_counter)&&(state == RXUL_STOP)); // 622 + cover((zero_baud_counter)&&(state == RXUL_WAIT)); // 626 + end +`endif + // }}} + //////////////////////////////////////////////////////////////////////// + // + // Properties to test via Verilator *and* formal + // {{{ + //////////////////////////////////////////////////////////////////////// + // +`ifdef FORMAL_VERILATOR + // FORMAL properties which can be tested via Verilator as well as + // Yosys FORMAL + always @(*) + assert((state == 4'hf)||(state <= RXUL_WAIT)); + always @(*) + assert(zero_baud_counter == (baud_counter == 0)? 1'b1:1'b0); + always @(*) + assert(baud_counter <= CLOCKS_PER_BAUD-1'b1); + // }}} +`endif +// }}} +endmodule
diff --git a/verilog/rtl/wbuart32/skidbuffer.v b/verilog/rtl/wbuart32/skidbuffer.v new file mode 100644 index 0000000..9ed3986 --- /dev/null +++ b/verilog/rtl/wbuart32/skidbuffer.v
@@ -0,0 +1,342 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Filename: skidbuffer.v +// {{{ +// Project: wbuart32, a full featured UART with simulator +// +// Purpose: A basic SKID buffer. +// +// Skid buffers are required for high throughput AXI code, since the AXI +// specification requires that all outputs be registered. This means +// that, if there are any stall conditions calculated, it will take a clock +// cycle before the stall can be propagated up stream. This means that +// the data will need to be buffered for a cycle until the stall signal +// can make it to the output. +// +// Handling that buffer is the purpose of this core. +// +// On one end of this core, you have the i_valid and i_data inputs to +// connect to your bus interface. There's also a registered o_ready +// signal to signal stalls for the bus interface. +// +// The other end of the core has the same basic interface, but it isn't +// registered. This allows you to interact with the bus interfaces +// as though they were combinatorial logic, by interacting with this half +// of the core. +// +// If at any time the incoming !stall signal, i_ready, signals a stall, +// the incoming data is placed into a buffer. Internally, that buffer +// is held in r_data with the r_valid flag used to indicate that valid +// data is within it. +// +// Parameters: +// DW or data width +// In order to make this core generic, the width of the data in the +// skid buffer is parameterized +// +// OPT_LOWPOWER +// Forces both o_data and r_data to zero if the respective *VALID +// signal is also low. While this costs extra logic, it can also +// be used to guarantee that any unused values aren't toggling and +// therefore unnecessarily using power. +// +// This excess toggling can be particularly problematic if the +// bus signals have a high fanout rate, or a long signal path +// across an FPGA. +// +// OPT_OUTREG +// Causes the outputs to be registered +// +// OPT_PASSTHROUGH +// Turns the skid buffer into a passthrough. Used for formal +// verification only. +// +// Creator: Dan Gisselquist, Ph.D. +// Gisselquist Technology, LLC +// +//////////////////////////////////////////////////////////////////////////////// +// }}} +// Copyright (C) 2019-2021, Gisselquist Technology, LLC +// {{{ +// This program is free software (firmware): you can redistribute it and/or +// modify it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program. (It's in the $(ROOT)/doc directory. Run make with no +// target there if the PDF file isn't present.) If not, see +// <http://www.gnu.org/licenses/> for a copy. +// +// License: GPL, v3, as defined and found on www.gnu.org, +// http://www.gnu.org/licenses/gpl.html +// +// +//////////////////////////////////////////////////////////////////////////////// +// +// +//`default_nettype none +// }}} +module skidbuffer #( + // {{{ + parameter [0:0] OPT_LOWPOWER = 0, + parameter [0:0] OPT_OUTREG = 1, + // + parameter [0:0] OPT_PASSTHROUGH = 0, + parameter DW = 8 + // }}} + ) ( + // {{{ + input wire i_clk, i_reset, + input wire i_valid, + output reg o_ready, + input wire [DW-1:0] i_data, + output reg o_valid, + input wire i_ready, + output reg [DW-1:0] o_data + // }}} + ); + + reg [DW-1:0] r_data; + + generate if (OPT_PASSTHROUGH) + begin : PASSTHROUGH + // {{{ + always @(*) + o_ready = i_ready; + always @(*) + o_valid = i_valid; + always @(*) + if (!i_valid && OPT_LOWPOWER) + o_data = 0; + else + o_data = i_data; + + always @(*) + r_data = 0; + // }}} + end else begin : LOGIC + // We'll start with skid buffer itself + // {{{ + reg r_valid; + + // r_valid + // {{{ + initial r_valid = 0; + always @(posedge i_clk) + if (i_reset) + r_valid <= 0; + else if ((i_valid && o_ready) && (o_valid && !i_ready)) + // We have incoming data, but the output is stalled + r_valid <= 1; + else if (i_ready) + r_valid <= 0; + // }}} + + // r_data + // {{{ + initial r_data = 0; + always @(posedge i_clk) + if (OPT_LOWPOWER && i_reset) + r_data <= 0; + else if (OPT_LOWPOWER && (!o_valid || i_ready)) + r_data <= 0; + else if ((!OPT_LOWPOWER || !OPT_OUTREG || i_valid) && o_ready) + r_data <= i_data; + // }}} + + // o_ready + // {{{ + always @(*) + o_ready = !r_valid; + // }}} + + // + // And then move on to the output port + // + if (!OPT_OUTREG) + begin + + always @(*) + o_valid = !i_reset && (i_valid || r_valid); + // }}} + + // o_data + // {{{ + always @(*) + if (r_valid) + o_data = r_data; + else if (!OPT_LOWPOWER || i_valid) + o_data = i_data; + else + o_data = 0; + // }}} + // }}} + end else begin : REG_OUTPUT + // Register our outputs + // {{{ + // o_valid + // {{{ + initial o_valid = 0; + always @(posedge i_clk) + if (i_reset) + o_valid <= 0; + else if (!o_valid || i_ready) + o_valid <= (i_valid || r_valid); + // }}} + + // o_data + // {{{ + initial o_data = 0; + always @(posedge i_clk) + if (OPT_LOWPOWER && i_reset) + o_data <= 0; + else if (!o_valid || i_ready) + begin + + if (r_valid) + o_data <= r_data; + else if (!OPT_LOWPOWER || i_valid) + o_data <= i_data; + else + o_data <= 0; + end + // }}} + + // }}} + end + // }}} + end endgenerate + +`ifdef FORMAL +`ifdef VERIFIC +`define FORMAL_VERIFIC +`endif +`endif +// +`ifdef FORMAL_VERIFIC + // Reset properties + property RESET_CLEARS_IVALID; + @(posedge i_clk) i_reset |=> !i_valid; + endproperty + + property IDATA_HELD_WHEN_NOT_READY; + @(posedge i_clk) disable iff (i_reset) + i_valid && !o_ready |=> i_valid && $stable(i_data); + endproperty + +`ifdef SKIDBUFFER + assume property (IDATA_HELD_WHEN_NOT_READY); +`else + assert property (IDATA_HELD_WHEN_NOT_READY); +`endif + + generate if (!OPT_PASSTHROUGH) + begin + + assert property (@(posedge i_clk) + OPT_OUTREG && i_reset |=> o_ready && !o_valid); + + assert property (@(posedge i_clk) + !OPT_OUTREG && i_reset |-> !o_valid); + + // Rule #1: + // Once o_valid goes high, the data cannot change until the + // clock after i_ready + assert property (@(posedge i_clk) + disable iff (i_reset) + o_valid && !i_ready + |=> (o_valid && $stable(o_data))); + + // Rule #2: + // All incoming data must either go directly to the + // output port, or into the skid buffer + assert property (@(posedge i_clk) + disable iff (i_reset) + (i_valid && o_ready + && (!OPT_OUTREG || o_valid) && !i_ready) + |=> (!o_ready && r_data == $past(i_data))); + + // Rule #3: + // After the last transaction, o_valid should become idle + if (!OPT_OUTREG) + begin + + assert property (@(posedge i_clk) + disable iff (i_reset) + i_ready |=> (o_valid == i_valid)); + + end else begin + + assert property (@(posedge i_clk) + disable iff (i_reset) + i_valid && o_ready |=> o_valid); + + assert property (@(posedge i_clk) + disable iff (i_reset) + !i_valid && o_ready && i_ready |=> !o_valid); + + end + + // Rule #4 + // Same thing, but this time for r_valid + assert property (@(posedge i_clk) + !o_ready && i_ready |=> o_ready); + + + if (OPT_LOWPOWER) + begin + // + // If OPT_LOWPOWER is set, o_data and r_data both need + // to be zero any time !o_valid or !r_valid respectively + assert property (@(posedge i_clk) + (OPT_OUTREG || !i_reset) && !o_valid |-> o_data == 0); + + assert property (@(posedge i_clk) + o_ready |-> r_data == 0); + + // else + // if OPT_LOWPOWER isn't set, we can lower our + // logic count by not forcing these values to zero. + end + +`ifdef SKIDBUFFER + reg f_changed_data; + + // Cover test + cover property (@(posedge i_clk) + disable iff (i_reset) + (!o_valid && !i_valid) + ##1 i_valid && i_ready [*3] + ##1 i_valid && !i_ready + ##1 i_valid && i_ready [*2] + ##1 i_valid && !i_ready [*2] + ##1 i_valid && i_ready [*3] + // Wait for the design to clear + ##1 o_valid && i_ready [*0:5] + ##1 (!o_valid && !i_valid && f_changed_data)); + + initial f_changed_data = 0; + always @(posedge i_clk) + if (i_reset) + f_changed_data <= 1; + else if (i_valid && $past(!i_valid || o_ready)) + begin + if (i_data != $past(i_data + 1)) + f_changed_data <= 0; + end else if (!i_valid && i_data != 0) + f_changed_data <= 0; + +`endif // SKIDCOVER + end endgenerate + +`endif // FORMAL_VERIFIC +endmodule +`ifndef YOSYS +`default_nettype wire +`endif
diff --git a/verilog/rtl/wbuart32/txuart.v b/verilog/rtl/wbuart32/txuart.v new file mode 100644 index 0000000..dbc5cff --- /dev/null +++ b/verilog/rtl/wbuart32/txuart.v
@@ -0,0 +1,1217 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Filename: txuart.v +// {{{ +// Project: wbuart32, a full featured UART with simulator +// +// Purpose: Transmit outputs over a single UART line. +// +// To interface with this module, connect it to your system clock, +// pass it the 32 bit setup register (defined below) and the byte +// of data you wish to transmit. Strobe the i_wr line high for one +// clock cycle, and your data will be off. Wait until the 'o_busy' +// line is low before strobing the i_wr line again--this implementation +// has NO BUFFER, so strobing i_wr while the core is busy will just +// cause your data to be lost. The output will be placed on the o_txuart +// output line. If you wish to set/send a break condition, assert the +// i_break line otherwise leave it low. +// +// There is a synchronous reset line, logic high. +// +// Now for the setup register. The register is 32 bits, so that this +// UART may be set up over a 32-bit bus. +// +// i_setup[30] Set this to zero to use hardware flow control, and to +// one to ignore hardware flow control. Only works if the hardware +// flow control has been properly wired. +// +// If you don't want hardware flow control, fix the i_rts bit to +// 1'b1, and let the synthesys tools optimize out the logic. +// +// i_setup[29:28] Indicates the number of data bits per word. This will +// either be 2'b00 for an 8-bit word, 2'b01 for a 7-bit word, 2'b10 +// for a six bit word, or 2'b11 for a five bit word. +// +// i_setup[27] Indicates whether or not to use one or two stop bits. +// Set this to one to expect two stop bits, zero for one. +// +// i_setup[26] Indicates whether or not a parity bit exists. Set this +// to 1'b1 to include parity. +// +// i_setup[25] Indicates whether or not the parity bit is fixed. Set +// to 1'b1 to include a fixed bit of parity, 1'b0 to allow the +// parity to be set based upon data. (Both assume the parity +// enable value is set.) +// +// i_setup[24] This bit is ignored if parity is not used. Otherwise, +// in the case of a fixed parity bit, this bit indicates whether +// mark (1'b1) or space (1'b0) parity is used. Likewise if the +// parity is not fixed, a 1'b1 selects even parity, and 1'b0 +// selects odd. +// +// i_setup[23:0] Indicates the speed of the UART in terms of clocks. +// So, for example, if you have a 200 MHz clock and wish to +// run your UART at 9600 baud, you would take 200 MHz and divide +// by 9600 to set this value to 24'd20834. Likewise if you wished +// to run this serial port at 115200 baud from a 200 MHz clock, +// you would set the value to 24'd1736 +// +// Thus, to set the UART for the common setting of an 8-bit word, +// one stop bit, no parity, and 115200 baud over a 200 MHz clock, you +// would want to set the setup value to: +// +// 32'h0006c8 // For 115,200 baud, 8 bit, no parity +// 32'h005161 // For 9600 baud, 8 bit, no parity +// +// +// Creator: Dan Gisselquist, Ph.D. +// Gisselquist Technology, LLC +// +//////////////////////////////////////////////////////////////////////////////// +// }}} +// Copyright (C) 2015-2021, Gisselquist Technology, LLC +// {{{ +// This program is free software (firmware): you can redistribute it and/or +// modify it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program. (It's in the $(ROOT)/doc directory. Run make with no +// target there if the PDF file isn't present.) If not, see +// <http://www.gnu.org/licenses/> for a copy. +// +// License: GPL, v3, as defined and found on www.gnu.org, +// http://www.gnu.org/licenses/gpl.html +// +// +//////////////////////////////////////////////////////////////////////////////// +// +// +//`default_nettype none +// +// }}} +module txuart #( + // {{{ + parameter [30:0] INITIAL_SETUP = 31'd868 + // + + // }}} + ) ( + // {{{ + input wire i_clk, i_reset, + input wire [30:0] i_setup, + input wire i_break, + input wire i_wr, + input wire [7:0] i_data, + // Hardware flow control Ready-To-Send bit. Set this to one to + // use the core without flow control. (A more appropriate name + // would be the Ready-To-Receive bit ...) + input wire i_cts_n, + // And the UART input line itself + output reg o_uart_tx, + // A line to tell others when we are ready to accept data. If + // (i_wr)&&(!o_busy) is ever true, then the core has accepted a + // byte for transmission. + output wire o_busy + // }}} + ); + + localparam [3:0] TXU_BIT_ZERO = 4'h0; + localparam [3:0] TXU_BIT_ONE = 4'h1; + localparam [3:0] TXU_BIT_TWO = 4'h2; + localparam [3:0] TXU_BIT_THREE = 4'h3; + // localparam [3:0] TXU_BIT_FOUR = 4'h4, + // localparam [3:0] TXU_BIT_FIVE = 4'h5, + // localparam [3:0] TXU_BIT_SIX = 4'h6, + localparam [3:0] TXU_BIT_SEVEN = 4'h7; + localparam [3:0] TXU_PARITY = 4'h8; + localparam [3:0] TXU_STOP = 4'h9; + localparam [3:0] TXU_SECOND_STOP = 4'ha; + // + localparam [3:0] TXU_BREAK = 4'he; + localparam [3:0] TXU_IDLE = 4'hf; + + + + // Signal declarations + // {{{ + wire [27:0] clocks_per_baud, break_condition; + wire [1:0] i_data_bits, data_bits; + wire use_parity, parity_odd, dblstop, fixd_parity, + fixdp_value, hw_flow_control, i_parity_odd; + reg [30:0] r_setup; + assign clocks_per_baud = { 4'h0, r_setup[23:0] }; + assign break_condition = { r_setup[23:0], 4'h0 }; + assign hw_flow_control = !r_setup[30]; + assign i_data_bits = i_setup[29:28]; + assign data_bits = r_setup[29:28]; + assign dblstop = r_setup[27]; + assign use_parity = r_setup[26]; + assign fixd_parity = r_setup[25]; + assign i_parity_odd = i_setup[24]; + assign parity_odd = r_setup[24]; + assign fixdp_value = r_setup[24]; + + reg [27:0] baud_counter; + reg [3:0] state; + reg [7:0] lcl_data; + reg calc_parity, r_busy, zero_baud_counter, last_state; + reg q_cts_n, qq_cts_n, ck_cts; + // }}} + + // CTS: ck_cts + // {{{ + // First step ... handle any hardware flow control, if so enabled. + // + // Clock in the flow control data, two clocks to avoid metastability + // Default to using hardware flow control (uart_setup[30]==0 to use it). + // Set this high order bit off if you do not wish to use it. + // + // While we might wish to give initial values to q_rts and ck_cts, + // 1) it's not required since the transmitter starts in a long wait + // state, and 2) doing so will prevent the synthesizer from optimizing + // this pin in the case it is hard set to 1'b1 external to this + // peripheral. + // + // initial q_cts_n = 1'b1; + // initial qq_cts_n = 1'b1; + // initial ck_cts = 1'b0; + always @(posedge i_clk) + { qq_cts_n, q_cts_n } <= { q_cts_n, i_cts_n }; + always @(posedge i_clk) + ck_cts <= (!qq_cts_n)||(!hw_flow_control); + // }}} + + // r_busy, state + // {{{ + initial r_busy = 1'b1; + initial state = TXU_IDLE; + always @(posedge i_clk) + if (i_reset) + begin + r_busy <= 1'b1; + state <= TXU_IDLE; + end else if (i_break) + begin + state <= TXU_BREAK; + r_busy <= 1'b1; + end else if (!zero_baud_counter) + begin // r_busy needs to be set coming into here + r_busy <= 1'b1; + end else if (state == TXU_BREAK) + begin + state <= TXU_IDLE; + r_busy <= !ck_cts; + end else if (state == TXU_IDLE) // STATE_IDLE + begin + if ((i_wr)&&(!r_busy)) + begin // Immediately start us off with a start bit + r_busy <= 1'b1; + case(i_data_bits) + 2'b00: state <= TXU_BIT_ZERO; + 2'b01: state <= TXU_BIT_ONE; + 2'b10: state <= TXU_BIT_TWO; + 2'b11: state <= TXU_BIT_THREE; + endcase + end else begin // Stay in idle + r_busy <= !ck_cts; + end + end else begin + // One clock tick in each of these states ... + // baud_counter <= clocks_per_baud - 28'h01; + r_busy <= 1'b1; + if (state[3] == 0) // First 8 bits + begin + if (state == TXU_BIT_SEVEN) + state <= (use_parity)? TXU_PARITY:TXU_STOP; + else + state <= state + 1; + end else if (state == TXU_PARITY) + begin + state <= TXU_STOP; + end else if (state == TXU_STOP) + begin // two stop bit(s) + if (dblstop) + state <= TXU_SECOND_STOP; + else + state <= TXU_IDLE; + end else // `TXU_SECOND_STOP and default: + begin + state <= TXU_IDLE; // Go back to idle + // Still r_busy, since we need to wait + // for the baud clock to finish counting + // out this last bit. + end + end + // }}} + + // o_busy + // {{{ + // This is a wire, designed to be true is we are ever busy above. + // originally, this was going to be true if we were ever not in the + // idle state. The logic has since become more complex, hence we have + // a register dedicated to this and just copy out that registers value. + assign o_busy = (r_busy); + // }}} + + // r_setup + // {{{ + // Our setup register. Accept changes between any pair of transmitted + // words. The register itself has many fields to it. These are + // broken out up top, and indicate what 1) our baud rate is, 2) our + // number of stop bits, 3) what type of parity we are using, and 4) + // the size of our data word. + initial r_setup = INITIAL_SETUP; + always @(posedge i_clk) + if (!o_busy) + r_setup <= i_setup; + // }}} + + // lcl_data + // {{{ + // This is our working copy of the i_data register which we use + // when transmitting. It is only of interest during transmit, and is + // allowed to be whatever at any other time. Hence, if r_busy isn't + // true, we can always set it. On the one clock where r_busy isn't + // true and i_wr is, we set it and r_busy is true thereafter. + // Then, on any zero_baud_counter (i.e. change between baud intervals) + // we simple logically shift the register right to grab the next bit. + initial lcl_data = 8'hff; + always @(posedge i_clk) + if (!r_busy) + lcl_data <= i_data; + else if (zero_baud_counter) + lcl_data <= { 1'b0, lcl_data[7:1] }; + // }}} + + // o_uart_tx + // {{{ + // This is the final result/output desired of this core. It's all + // centered about o_uart_tx. This is what finally needs to follow + // the UART protocol. + // + // Ok, that said, our rules are: + // 1'b0 on any break condition + // 1'b0 on a start bit (IDLE, write, and not busy) + // lcl_data[0] during any data transfer, but only at the baud + // change + // PARITY -- During the parity bit. This depends upon whether or + // not the parity bit is fixed, then what it's fixed to, + // or changing, and hence what it's calculated value is. + // 1'b1 at all other times (stop bits, idle, etc) + + initial o_uart_tx = 1'b1; + always @(posedge i_clk) + if (i_reset) + o_uart_tx <= 1'b1; + else if ((i_break)||((i_wr)&&(!r_busy))) + o_uart_tx <= 1'b0; + else if (zero_baud_counter) + casez(state) + 4'b0???: o_uart_tx <= lcl_data[0]; + TXU_PARITY: o_uart_tx <= calc_parity; + default: o_uart_tx <= 1'b1; + endcase + // }}} + + // calc_parity + // {{{ + // Calculate the parity to be placed into the parity bit. If the + // parity is fixed, then the parity bit is given by the fixed parity + // value (r_setup[24]). Otherwise the parity is given by the GF2 + // sum of all the data bits (plus one for even parity). + initial calc_parity = 1'b0; + always @(posedge i_clk) + if (!o_busy) + calc_parity <= i_setup[24]; + else if (fixd_parity) + calc_parity <= fixdp_value; + else if (zero_baud_counter) + begin + if (state[3] == 0) // First 8 bits of msg + calc_parity <= calc_parity ^ lcl_data[0]; + else if (state == TXU_IDLE) + calc_parity <= parity_odd; + end else if (!r_busy) + calc_parity <= parity_odd; + // }}} + + // baud_counter, zero_baud_counter + // {{{ + // All of the above logic is driven by the baud counter. Bits must last + // {{{ + // clocks_per_baud in length, and this baud counter is what we use to + // make certain of that. + // + // The basic logic is this: at the beginning of a bit interval, start + // the baud counter and set it to count clocks_per_baud. When it gets + // to zero, restart it. + // + // However, comparing a 28'bit number to zero can be rather complex-- + // especially if we wish to do anything else on that same clock. For + // that reason, we create "zero_baud_counter". zero_baud_counter is + // nothing more than a flag that is true anytime baud_counter is zero. + // It's true when the logic (above) needs to step to the next bit. + // Simple enough? + // + // I wish we could stop there, but there are some other (ugly) + // conditions to deal with that offer exceptions to this basic logic. + // + // 1. When the user has commanded a BREAK across the line, we need to + // wait several baud intervals following the break before we start + // transmitting, to give any receiver a chance to recognize that we are + // out of the break condition, and to know that the next bit will be + // a stop bit. + // + // 2. A reset is similar to a break condition--on both we wait several + // baud intervals before allowing a start bit. + // + // 3. In the idle state, we stop our counter--so that upon a request + // to transmit when idle we can start transmitting immediately, rather + // than waiting for the end of the next (fictitious and arbitrary) baud + // interval. + // + // When (i_wr)&&(!r_busy)&&(state == TXU_IDLE) then we're not only in + // the idle state, but we also just accepted a command to start writing + // the next word. At this point, the baud counter needs to be reset + // to the number of clocks per baud, and zero_baud_counter set to zero. + // + // The logic is a bit twisted here, in that it will only check for the + // above condition when zero_baud_counter is false--so as to make + // certain the STOP bit is complete. + // }}} + initial zero_baud_counter = 1'b0; + initial baud_counter = 28'h05; + always @(posedge i_clk) + begin + zero_baud_counter <= (baud_counter == 28'h01); + if ((i_reset)||(i_break)) + begin + // Give ourselves 16 bauds before being ready + baud_counter <= break_condition; + zero_baud_counter <= 1'b0; + end else if (!zero_baud_counter) + baud_counter <= baud_counter - 28'h01; + else if (state == TXU_BREAK) + begin + baud_counter <= 0; + zero_baud_counter <= 1'b1; + end else if (state == TXU_IDLE) + begin + baud_counter <= 28'h0; + zero_baud_counter <= 1'b1; + if ((i_wr)&&(!r_busy)) + begin + baud_counter <= { 4'h0, i_setup[23:0]} - 28'h01; + zero_baud_counter <= 1'b0; + end + end else if (last_state) + baud_counter <= clocks_per_baud - 28'h02; + else + baud_counter <= clocks_per_baud - 28'h01; + end + // }}} + + // last_state + // {{{ + initial last_state = 1'b0; + always @(posedge i_clk) + if (dblstop) + last_state <= (state == TXU_SECOND_STOP); + else + last_state <= (state == TXU_STOP); + // }}} + + // Make Verilator happy + // {{{ + // Verilator lint_off UNUSED + wire unused; + assign unused = &{ 1'b0, i_parity_odd, data_bits }; + // Verilator lint_on UNUSED + // }}} +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// +// Formal properties +// {{{ +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +`ifdef FORMAL + // Declarations + // {{{ + reg fsv_parity; + reg [30:0] fsv_setup; + reg [7:0] fsv_data; + reg f_past_valid; + // + // Our various sequence data declarations + reg [5:0] f_five_seq; + reg [6:0] f_six_seq; + reg [7:0] f_seven_seq; + reg [8:0] f_eight_seq; + reg [2:0] f_stop_seq; // parity bit, stop bit, double stop bit + // }}} + + initial f_past_valid = 1'b0; + always @(posedge i_clk) + f_past_valid <= 1'b1; + + always @(posedge i_clk) + if ((i_wr)&&(!o_busy)) + fsv_data <= i_data; + + initial fsv_setup = INITIAL_SETUP; + always @(posedge i_clk) + if (!o_busy) + fsv_setup <= i_setup; + + always @(*) + assert(r_setup == fsv_setup); + + + always @(posedge i_clk) + assert(zero_baud_counter == (baud_counter == 0)); + + always @(*) + if (!o_busy) + assert(zero_baud_counter); + + /* + * + * Will only pass if !i_break && !i_reset, otherwise the setup can + * change in the middle of this operation + * + always @(posedge i_clk) + if ((f_past_valid)&&(!$past(i_reset))&&(!$past(i_break)) + &&(($past(o_busy))||($past(i_wr)))) + assert(baud_counter <= { fsv_setup[23:0], 4'h0 }); + */ + + // A single baud interval + always @(posedge i_clk) + if ((f_past_valid)&&(!$past(zero_baud_counter)) + &&(!$past(i_reset))&&(!$past(i_break))) + begin + assert($stable(o_uart_tx)); + assert($stable(state)); + assert($stable(lcl_data)); + if ((state != TXU_IDLE)&&(state != TXU_BREAK)) + assert($stable(calc_parity)); + assert(baud_counter == $past(baud_counter)-1'b1); + end + + + // + // One byte transmitted + // + // DATA = the byte that is sent + // CKS = the number of clocks per bit + // + //////////////////////////////////////////////////////////////////////// + // + // Five bit data + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + + initial f_five_seq = 0; + always @(posedge i_clk) + if ((i_reset)||(i_break)) + f_five_seq = 0; + else if ((state == TXU_IDLE)&&(i_wr)&&(!o_busy) + &&(i_data_bits == 2'b11)) // five data bits + f_five_seq <= 1; + else if (zero_baud_counter) + f_five_seq <= f_five_seq << 1; + + always @(*) + if (|f_five_seq) + begin + assert(fsv_setup[29:28] == data_bits); + assert(data_bits == 2'b11); + assert(baud_counter < fsv_setup[23:0]); + + assert(1'b0 == |f_six_seq); + assert(1'b0 == |f_seven_seq); + assert(1'b0 == |f_eight_seq); + assert(r_busy); + assert(state > 4'h2); + end + + always @(*) + case(f_five_seq) + 6'h00: begin assert(1); end + 6'h01: begin + assert(state == 4'h3); + assert(o_uart_tx == 1'b0); + assert(lcl_data[4:0] == fsv_data[4:0]); + if (!fixd_parity) + assert(calc_parity == parity_odd); + end + 6'h02: begin + assert(state == 4'h4); + assert(o_uart_tx == fsv_data[0]); + assert(lcl_data[3:0] == fsv_data[4:1]); + if (!fixd_parity) + assert(calc_parity == fsv_data[0] ^ parity_odd); + end + 6'h04: begin + assert(state == 4'h5); + assert(o_uart_tx == fsv_data[1]); + assert(lcl_data[2:0] == fsv_data[4:2]); + if (!fixd_parity) + assert(calc_parity == (^fsv_data[1:0]) ^ parity_odd); + end + 6'h08: begin + assert(state == 4'h6); + assert(o_uart_tx == fsv_data[2]); + assert(lcl_data[1:0] == fsv_data[4:3]); + if (!fixd_parity) + assert(calc_parity == (^fsv_data[2:0]) ^ parity_odd); + end + 6'h10: begin + assert(state == 4'h7); + assert(o_uart_tx == fsv_data[3]); + assert(lcl_data[0] == fsv_data[4]); + if (!fixd_parity) + assert(calc_parity == (^fsv_data[3:0]) ^ parity_odd); + end + 6'h20: begin + if (use_parity) + assert(state == 4'h8); + else + assert(state == 4'h9); + assert(o_uart_tx == fsv_data[4]); + if (!fixd_parity) + assert(calc_parity == (^fsv_data[4:0]) ^ parity_odd); + end + default: begin assert(0); end + endcase + // }}} + //////////////////////////////////////////////////////////////////////// + // + // Six bit data + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + + initial f_six_seq = 0; + always @(posedge i_clk) + if ((i_reset)||(i_break)) + f_six_seq = 0; + else if ((state == TXU_IDLE)&&(i_wr)&&(!o_busy) + &&(i_data_bits == 2'b10)) // six data bits + f_six_seq <= 1; + else if (zero_baud_counter) + f_six_seq <= f_six_seq << 1; + + always @(*) + if (|f_six_seq) + begin + assert(fsv_setup[29:28] == 2'b10); + assert(fsv_setup[29:28] == data_bits); + assert(baud_counter < fsv_setup[23:0]); + + assert(1'b0 == |f_five_seq); + assert(1'b0 == |f_seven_seq); + assert(1'b0 == |f_eight_seq); + assert(r_busy); + assert(state > 4'h1); + end + + always @(*) + case(f_six_seq) + 7'h00: begin assert(1); end + 7'h01: begin + assert(state == 4'h2); + assert(o_uart_tx == 1'b0); + assert(lcl_data[5:0] == fsv_data[5:0]); + if (!fixd_parity) + assert(calc_parity == parity_odd); + end + 7'h02: begin + assert(state == 4'h3); + assert(o_uart_tx == fsv_data[0]); + assert(lcl_data[4:0] == fsv_data[5:1]); + if (!fixd_parity) + assert(calc_parity == fsv_data[0] ^ parity_odd); + end + 7'h04: begin + assert(state == 4'h4); + assert(o_uart_tx == fsv_data[1]); + assert(lcl_data[3:0] == fsv_data[5:2]); + if (!fixd_parity) + assert(calc_parity == (^fsv_data[1:0]) ^ parity_odd); + end + 7'h08: begin + assert(state == 4'h5); + assert(o_uart_tx == fsv_data[2]); + assert(lcl_data[2:0] == fsv_data[5:3]); + if (!fixd_parity) + assert(calc_parity == (^fsv_data[2:0]) ^ parity_odd); + end + 7'h10: begin + assert(state == 4'h6); + assert(o_uart_tx == fsv_data[3]); + assert(lcl_data[1:0] == fsv_data[5:4]); + if (!fixd_parity) + assert(calc_parity == (^fsv_data[3:0]) ^ parity_odd); + end + 7'h20: begin + assert(state == 4'h7); + assert(lcl_data[0] == fsv_data[5]); + assert(o_uart_tx == fsv_data[4]); + if (!fixd_parity) + assert(calc_parity == ((^fsv_data[4:0]) ^ parity_odd)); + end + 7'h40: begin + if (use_parity) + assert(state == 4'h8); + else + assert(state == 4'h9); + assert(o_uart_tx == fsv_data[5]); + if (!fixd_parity) + assert(calc_parity == ((^fsv_data[5:0]) ^ parity_odd)); + end + default: begin if (f_past_valid) assert(0); end + endcase + // }}} + //////////////////////////////////////////////////////////////////////// + // + // Seven bit data + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + + initial f_seven_seq = 0; + always @(posedge i_clk) + if ((i_reset)||(i_break)) + f_seven_seq = 0; + else if ((state == TXU_IDLE)&&(i_wr)&&(!o_busy) + &&(i_data_bits == 2'b01)) // seven data bits + f_seven_seq <= 1; + else if (zero_baud_counter) + f_seven_seq <= f_seven_seq << 1; + + always @(*) + if (|f_seven_seq) + begin + assert(fsv_setup[29:28] == 2'b01); + assert(fsv_setup[29:28] == data_bits); + assert(baud_counter < fsv_setup[23:0]); + + assert(1'b0 == |f_five_seq); + assert(1'b0 == |f_six_seq); + assert(1'b0 == |f_eight_seq); + assert(r_busy); + assert(state != 4'h0); + end + + always @(*) + case(f_seven_seq) + 8'h00: begin assert(1); end + 8'h01: begin + assert(state == 4'h1); + assert(o_uart_tx == 1'b0); + assert(lcl_data[6:0] == fsv_data[6:0]); + if (!fixd_parity) + assert(calc_parity == parity_odd); + end + 8'h02: begin + assert(state == 4'h2); + assert(o_uart_tx == fsv_data[0]); + assert(lcl_data[5:0] == fsv_data[6:1]); + if (!fixd_parity) + assert(calc_parity == fsv_data[0] ^ parity_odd); + end + 8'h04: begin + assert(state == 4'h3); + assert(o_uart_tx == fsv_data[1]); + assert(lcl_data[4:0] == fsv_data[6:2]); + if (!fixd_parity) + assert(calc_parity == (^fsv_data[1:0]) ^ parity_odd); + end + 8'h08: begin + assert(state == 4'h4); + assert(o_uart_tx == fsv_data[2]); + assert(lcl_data[3:0] == fsv_data[6:3]); + if (!fixd_parity) + assert(calc_parity == (^fsv_data[2:0]) ^ parity_odd); + end + 8'h10: begin + assert(state == 4'h5); + assert(o_uart_tx == fsv_data[3]); + assert(lcl_data[2:0] == fsv_data[6:4]); + if (!fixd_parity) + assert(calc_parity == (^fsv_data[3:0]) ^ parity_odd); + end + 8'h20: begin + assert(state == 4'h6); + assert(o_uart_tx == fsv_data[4]); + assert(lcl_data[1:0] == fsv_data[6:5]); + if (!fixd_parity) + assert(calc_parity == ((^fsv_data[4:0]) ^ parity_odd)); + end + 8'h40: begin + assert(state == 4'h7); + assert(lcl_data[0] == fsv_data[6]); + assert(o_uart_tx == fsv_data[5]); + if (!fixd_parity) + assert(calc_parity == ((^fsv_data[5:0]) ^ parity_odd)); + end + 8'h80: begin + if (use_parity) + assert(state == 4'h8); + else + assert(state == 4'h9); + assert(o_uart_tx == fsv_data[6]); + if (!fixd_parity) + assert(calc_parity == ((^fsv_data[6:0]) ^ parity_odd)); + end + default: begin if (f_past_valid) assert(0); end + endcase + // }}} + //////////////////////////////////////////////////////////////////////// + // + // Eight bit data + // {{{ + //////////////////////////////////////////////////////////////////////// + initial f_eight_seq = 0; + always @(posedge i_clk) + if ((i_reset)||(i_break)) + f_eight_seq = 0; + else if ((state == TXU_IDLE)&&(i_wr)&&(!o_busy) + &&(i_data_bits == 2'b00)) // Eight data bits + f_eight_seq <= 1; + else if (zero_baud_counter) + f_eight_seq <= f_eight_seq << 1; + + always @(*) + if (|f_eight_seq) + begin + assert(fsv_setup[29:28] == 2'b00); + assert(fsv_setup[29:28] == data_bits); + assert(baud_counter < { 6'h0, fsv_setup[23:0]}); + + assert(1'b0 == |f_five_seq); + assert(1'b0 == |f_six_seq); + assert(1'b0 == |f_seven_seq); + assert(r_busy); + end + + always @(*) + case(f_eight_seq) + 9'h000: begin assert(1); end + 9'h001: begin + assert(state == 4'h0); + assert(o_uart_tx == 1'b0); + assert(lcl_data[7:0] == fsv_data[7:0]); + if (!fixd_parity) + assert(calc_parity == parity_odd); + end + 9'h002: begin + assert(state == 4'h1); + assert(o_uart_tx == fsv_data[0]); + assert(lcl_data[6:0] == fsv_data[7:1]); + if (!fixd_parity) + assert(calc_parity == fsv_data[0] ^ parity_odd); + end + 9'h004: begin + assert(state == 4'h2); + assert(o_uart_tx == fsv_data[1]); + assert(lcl_data[5:0] == fsv_data[7:2]); + if (!fixd_parity) + assert(calc_parity == (^fsv_data[1:0]) ^ parity_odd); + end + 9'h008: begin + assert(state == 4'h3); + assert(o_uart_tx == fsv_data[2]); + assert(lcl_data[4:0] == fsv_data[7:3]); + if (!fixd_parity) + assert(calc_parity == (^fsv_data[2:0]) ^ parity_odd); + end + 9'h010: begin + assert(state == 4'h4); + assert(o_uart_tx == fsv_data[3]); + assert(lcl_data[3:0] == fsv_data[7:4]); + if (!fixd_parity) + assert(calc_parity == (^fsv_data[3:0]) ^ parity_odd); + end + 9'h020: begin + assert(state == 4'h5); + assert(o_uart_tx == fsv_data[4]); + assert(lcl_data[2:0] == fsv_data[7:5]); + if (!fixd_parity) + assert(calc_parity == (^fsv_data[4:0]) ^ parity_odd); + end + 9'h040: begin + assert(state == 4'h6); + assert(o_uart_tx == fsv_data[5]); + assert(lcl_data[1:0] == fsv_data[7:6]); + if (!fixd_parity) + assert(calc_parity == (^fsv_data[5:0]) ^ parity_odd); + end + 9'h080: begin + assert(state == 4'h7); + assert(o_uart_tx == fsv_data[6]); + assert(lcl_data[0] == fsv_data[7]); + if (!fixd_parity) + assert(calc_parity == ((^fsv_data[6:0]) ^ parity_odd)); + end + 9'h100: begin + if (use_parity) + assert(state == 4'h8); + else + assert(state == 4'h9); + assert(o_uart_tx == fsv_data[7]); + if (!fixd_parity) + assert(calc_parity == ((^fsv_data[7:0]) ^ parity_odd)); + end + default: begin if (f_past_valid) assert(0); end + endcase + // }}} + //////////////////////////////////////////////////////////////////////// + // + // Combined properties for all of the data sequences + // {{{ + //////////////////////////////////////////////////////////////////////// + // + always @(posedge i_clk) + if (((|f_five_seq[5:0]) || (|f_six_seq[6:0]) || (|f_seven_seq[7:0]) + || (|f_eight_seq[8:0])) + && ($past(zero_baud_counter))) + assert(baud_counter == { 4'h0, fsv_setup[23:0] }-1); + + // }}} + //////////////////////////////////////////////////////////////////////// + // + // The stop sequence + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // This consists of any parity bit, as well as one or two stop bits + // + initial f_stop_seq = 1'b0; + always @(posedge i_clk) + if ((i_reset)||(i_break)) + f_stop_seq <= 0; + else if (zero_baud_counter) + begin + f_stop_seq <= 0; + if (f_stop_seq[0]) // Coming from a parity bit + begin + if (dblstop) + f_stop_seq[1] <= 1'b1; + else + f_stop_seq[2] <= 1'b1; + end + + if (f_stop_seq[1]) + f_stop_seq[2] <= 1'b1; + + if (f_eight_seq[8] | f_seven_seq[7] | f_six_seq[6] + | f_five_seq[5]) + begin + if (use_parity) + f_stop_seq[0] <= 1'b1; + else if (dblstop) + f_stop_seq[1] <= 1'b1; + else + f_stop_seq[2] <= 1'b1; + end + end + + always @(*) + if (|f_stop_seq) + begin + assert(1'b0 == |f_five_seq[4:0]); + assert(1'b0 == |f_six_seq[5:0]); + assert(1'b0 == |f_seven_seq[6:0]); + assert(1'b0 == |f_eight_seq[7:0]); + + assert(r_busy); + end + + always @(*) + if (f_stop_seq[0]) + begin + // 9 if dblstop and use_parity + if (dblstop) + assert(state == TXU_STOP); + else + assert(state == TXU_STOP); + assert(use_parity); + assert(o_uart_tx == fsv_parity); + end + + always @(*) + if (f_stop_seq[1]) + begin + // if (!use_parity) + assert(state == TXU_SECOND_STOP); + assert(dblstop); + assert(o_uart_tx); + end + + always @(*) + if (f_stop_seq[2]) + begin + assert(state == 4'hf); + assert(o_uart_tx); + assert(baud_counter < fsv_setup[23:0]-1'b1); + end + + + always @(*) + if (fsv_setup[25]) + fsv_parity <= fsv_setup[24]; + else + case(fsv_setup[29:28]) + 2'b00: fsv_parity = (^fsv_data[7:0]) ^ fsv_setup[24]; + 2'b01: fsv_parity = (^fsv_data[6:0]) ^ fsv_setup[24]; + 2'b10: fsv_parity = (^fsv_data[5:0]) ^ fsv_setup[24]; + 2'b11: fsv_parity = (^fsv_data[4:0]) ^ fsv_setup[24]; + endcase + // }}} + ////////////////////////////////////////////////////////////////////// + // + // The break sequence + // {{{ + ////////////////////////////////////////////////////////////////////// + reg [1:0] f_break_seq; + + initial f_break_seq = 2'b00; + always @(posedge i_clk) + if (i_reset) + f_break_seq <= 2'b00; + else if (i_break) + f_break_seq <= 2'b01; + else if (!zero_baud_counter) + f_break_seq <= { |f_break_seq, 1'b0 }; + else + f_break_seq <= 0; + + always @(posedge i_clk) + if (f_break_seq[0]) + assert(baud_counter == { $past(fsv_setup[23:0]), 4'h0 }); + always @(posedge i_clk) + if ((f_past_valid)&&($past(f_break_seq[1]))&&(state != TXU_BREAK)) + begin + assert(state == TXU_IDLE); + assert(o_uart_tx == 1'b1); + end + + always @(*) + if (|f_break_seq) + begin + assert(state == TXU_BREAK); + assert(r_busy); + assert(o_uart_tx == 1'b0); + end + // }}} + ////////////////////////////////////////////////////////////////////// + // + // Properties for use during induction if we are made a submodule of + // the rxuart + // {{{ + ////////////////////////////////////////////////////////////////////// + // + // Need enough bits for reset (24+4) plus enough bits for all of the + // various characters, 24+4, so 24+5 is a minimum of this counter + // +`ifndef TXUART + reg [28:0] f_counter; + initial f_counter = 0; + always @(posedge i_clk) + if (!o_busy) + f_counter <= 1'b0; + else + f_counter <= f_counter + 1'b1; + + always @(*) + if (f_five_seq[0]|f_six_seq[0]|f_seven_seq[0]|f_eight_seq[0]) + // {{{ + assert(f_counter == (fsv_setup[23:0] - baud_counter - 1)); + // }}} + else if (f_five_seq[1]|f_six_seq[1]|f_seven_seq[1]|f_eight_seq[1]) + // {{{ + assert(f_counter == ({4'h0, fsv_setup[23:0], 1'b0} - baud_counter - 1)); + // }}} + else if (f_five_seq[2]|f_six_seq[2]|f_seven_seq[2]|f_eight_seq[2]) + // {{{ + assert(f_counter == ({4'h0, fsv_setup[23:0], 1'b0} + +{5'h0, fsv_setup[23:0]} + - baud_counter - 1)); + // }}} + else if (f_five_seq[3]|f_six_seq[3]|f_seven_seq[3]|f_eight_seq[3]) + // {{{ + assert(f_counter == ({3'h0, fsv_setup[23:0], 2'b0} + - baud_counter - 1)); + // }}} + else if (f_five_seq[4]|f_six_seq[4]|f_seven_seq[4]|f_eight_seq[4]) + // {{{ + assert(f_counter == ({3'h0, fsv_setup[23:0], 2'b0} + +{5'h0, fsv_setup[23:0]} + - baud_counter - 1)); + // }}} + else if (f_five_seq[5]|f_six_seq[5]|f_seven_seq[5]|f_eight_seq[5]) + // {{{ + assert(f_counter == ({3'h0, fsv_setup[23:0], 2'b0} + +{4'h0, fsv_setup[23:0], 1'b0} + - baud_counter - 1)); + // }}} + else if (f_six_seq[6]|f_seven_seq[6]|f_eight_seq[6]) + // {{{ + assert(f_counter == ({3'h0, fsv_setup[23:0], 2'b0} + +{5'h0, fsv_setup[23:0]} + +{4'h0, fsv_setup[23:0], 1'b0} + - baud_counter - 1)); + // }}} + else if (f_seven_seq[7]|f_eight_seq[7]) + // {{{ + assert(f_counter == ({2'h0, fsv_setup[23:0], 3'b0} // 8 + - baud_counter - 1)); + // }}} + else if (f_eight_seq[8]) + // {{{ + assert(f_counter == ({2'h0, fsv_setup[23:0], 3'b0} // 9 + +{5'h0, fsv_setup[23:0]} + - baud_counter - 1)); + // }}} + else if (f_stop_seq[0] || (!use_parity && f_stop_seq[1])) + begin + // {{{ + // Parity bit, or first of two stop bits + case(data_bits) + 2'b00: assert(f_counter == ({2'h0, fsv_setup[23:0], 3'b0} + +{4'h0, fsv_setup[23:0], 1'b0} // 10 + - baud_counter - 1)); + 2'b01: assert(f_counter == ({2'h0, fsv_setup[23:0], 3'b0} + +{5'h0, fsv_setup[23:0]} // 9 + - baud_counter - 1)); + 2'b10: assert(f_counter == ({2'h0, fsv_setup[23:0], 3'b0} + - baud_counter - 1)); // 8 + 2'b11: assert(f_counter == ({3'h0, fsv_setup[23:0], 2'b0} + +{5'h0, fsv_setup[23:0]} // 7 + +{4'h0, fsv_setup[23:0], 1'b0} + - baud_counter - 1)); + endcase + // }}} + end else if (!use_parity && !dblstop && f_stop_seq[2]) + begin + // {{{ + // No parity, single stop bit + // Different from the one above, since the last counter is has + // one fewer items within it + case(data_bits) + 2'b00: assert(f_counter == ({2'h0, fsv_setup[23:0], 3'b0} + +{4'h0, fsv_setup[23:0], 1'b0} // 10 + - baud_counter - 2)); + 2'b01: assert(f_counter == ({2'h0, fsv_setup[23:0], 3'b0} + +{5'h0, fsv_setup[23:0]} // 9 + - baud_counter - 2)); + 2'b10: assert(f_counter == ({2'h0, fsv_setup[23:0], 3'b0} + - baud_counter - 2)); // 8 + 2'b11: assert(f_counter == ({3'h0, fsv_setup[23:0], 2'b0} + +{5'h0, fsv_setup[23:0]} // 7 + +{4'h0, fsv_setup[23:0], 1'b0} + - baud_counter - 2)); + endcase + // }}} + end else if (f_stop_seq[1]) + begin + // {{{ + // Parity and the first of two stop bits + assert(dblstop && use_parity); + case(data_bits) + 2'b00: assert(f_counter == ({2'h0, fsv_setup[23:0], 3'b0} + +{5'h0, fsv_setup[23:0]} // 11 + +{4'h0, fsv_setup[23:0], 1'b0} + - baud_counter - 1)); + 2'b01: assert(f_counter == ({2'h0, fsv_setup[23:0], 3'b0} + +{4'h0, fsv_setup[23:0], 1'b0} // 10 + - baud_counter - 1)); + 2'b10: assert(f_counter == ({2'h0, fsv_setup[23:0], 3'b0} + +{5'h0, fsv_setup[23:0]} // 9 + - baud_counter - 1)); + 2'b11: assert(f_counter == ({2'h0, fsv_setup[23:0], 3'b0} + - baud_counter - 1)); // 8 + endcase + // }}} + end else if ((dblstop ^ use_parity) && f_stop_seq[2]) + begin + // {{{ + // Parity and one stop bit + // assert(!dblstop && use_parity); + case(data_bits) + 2'b00: assert(f_counter == ({2'h0, fsv_setup[23:0], 3'b0} + +{5'h0, fsv_setup[23:0]} // 11 + +{4'h0, fsv_setup[23:0], 1'b0} + - baud_counter - 2)); + 2'b01: assert(f_counter == ({2'h0, fsv_setup[23:0], 3'b0} + +{4'h0, fsv_setup[23:0], 1'b0} // 10 + - baud_counter - 2)); + 2'b10: assert(f_counter == ({2'h0, fsv_setup[23:0], 3'b0} + +{5'h0, fsv_setup[23:0]} // 9 + - baud_counter - 2)); + 2'b11: assert(f_counter == ({2'h0, fsv_setup[23:0], 3'b0} + - baud_counter - 2)); // 8 + endcase + // }}} + end else if (f_stop_seq[2]) + begin + // {{{ + assert(dblstop); + assert(use_parity); + // Parity and two stop bits + case(data_bits) + 2'b00: assert(f_counter == ({2'h0, fsv_setup[23:0], 3'b0} + +{3'h0, fsv_setup[23:0], 2'b00} // 12 + - baud_counter - 2)); + 2'b01: assert(f_counter == ({2'h0, fsv_setup[23:0], 3'b0} + +{5'h0, fsv_setup[23:0]} // 11 + +{4'h0, fsv_setup[23:0], 1'b0} + - baud_counter - 2)); + 2'b10: assert(f_counter == ({2'h0, fsv_setup[23:0], 3'b0} + +{4'h0, fsv_setup[23:0], 1'b0} // 10 + - baud_counter - 2)); + 2'b11: assert(f_counter == ({2'h0, fsv_setup[23:0], 3'b0} + +{5'h0, fsv_setup[23:0]} // 9 + - baud_counter - 2)); + endcase + // }}} + end +`endif + // }}} + ////////////////////////////////////////////////////////////////////// + // + // Other properties, not necessarily associated with any sequences + // + ////////////////////////////////////////////////////////////////////// + always @(*) + assert((state < 4'hb)||(state >= 4'he)); + ////////////////////////////////////////////////////////////////////// + // + // Careless/limiting assumption section + // + ////////////////////////////////////////////////////////////////////// + always @(*) + assume(i_setup[23:0] > 2); + always @(*) + assert(fsv_setup[23:0] > 2); + +`endif // FORMAL +// }}} +endmodule +
diff --git a/verilog/rtl/wbuart32/txuartlite.v b/verilog/rtl/wbuart32/txuartlite.v new file mode 100644 index 0000000..05b19a1 --- /dev/null +++ b/verilog/rtl/wbuart32/txuartlite.v
@@ -0,0 +1,461 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Filename: txuartlite.v +// {{{ +// Project: wbuart32, a full featured UART with simulator +// +// Purpose: Transmit outputs over a single UART line. This particular UART +// implementation has been extremely simplified: it does not handle +// generating break conditions, nor does it handle anything other than the +// 8N1 (8 data bits, no parity, 1 stop bit) UART sub-protocol. +// +// To interface with this module, connect it to your system clock, and +// pass it the byte of data you wish to transmit. Strobe the i_wr line +// high for one cycle, and your data will be off. Wait until the 'o_busy' +// line is low before strobing the i_wr line again--this implementation +// has NO BUFFER, so strobing i_wr while the core is busy will just +// get ignored. The output will be placed on the o_txuart output line. +// +// (I often set both data and strobe on the same clock, and then just leave +// them set until the busy line is low. Then I move on to the next piece +// of data.) +// +// Creator: Dan Gisselquist, Ph.D. +// Gisselquist Technology, LLC +// +//////////////////////////////////////////////////////////////////////////////// +// }}} +// Copyright (C) 2015-2021, Gisselquist Technology, LLC +// {{{ +// This program is free software (firmware): you can redistribute it and/or +// modify it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program. (It's in the $(ROOT)/doc directory. Run make with no +// target there if the PDF file isn't present.) If not, see +// <http://www.gnu.org/licenses/> for a copy. +// +// License: GPL, v3, as defined and found on www.gnu.org, +// http://www.gnu.org/licenses/gpl.html +// +// +//////////////////////////////////////////////////////////////////////////////// +// +// +//`default_nettype none +// }}} +module txuartlite #( + // {{{ + // TIMING_BITS -- the number of bits required to represent + // the number of clocks per baud. 24 should be sufficient for + // most baud rates, but you can trim it down to save logic if + // you would like. TB is just an abbreviation for TIMING_BITS. + parameter [4:0] TIMING_BITS = 5'd24, + // CLOCKS_PER_BAUD -- the number of system clocks per baud + // interval. + parameter [(TB-1):0] CLOCKS_PER_BAUD = 8 // 24'd868 + // }}} + ) ( + // {{{ + input wire i_clk, + input wire i_wr, + input wire [7:0] i_data, + // And the UART input line itself + output reg o_uart_tx, + // A line to tell others when we are ready to accept data. If + // (i_wr)&&(!o_busy) is ever true, then the core has accepted + // a byte for transmission. + output wire o_busy + // }}} + ); + + localparam TB = TIMING_BITS; + + // Register/net declarations + // {{{ + localparam [3:0] TXUL_BIT_ZERO = 4'h0, + // TXUL_BIT_ONE = 4'h1, + // TXUL_BIT_TWO = 4'h2, + // TXUL_BIT_THREE = 4'h3, + // TXUL_BIT_FOUR = 4'h4, + // TXUL_BIT_FIVE = 4'h5, + // TXUL_BIT_SIX = 4'h6, + // TXUL_BIT_SEVEN = 4'h7, + TXUL_STOP = 4'h8, + TXUL_IDLE = 4'hf; + + reg [(TB-1):0] baud_counter; + reg [3:0] state; + reg [7:0] lcl_data; + reg r_busy, zero_baud_counter; + // }}} + + // Big state machine controlling: r_busy, state + // {{{ + // + initial r_busy = 1'b1; + initial state = TXUL_IDLE; + always @(posedge i_clk) + begin + if (!zero_baud_counter) + // r_busy needs to be set coming into here + r_busy <= 1'b1; + else if (state > TXUL_STOP) // STATE_IDLE + begin + state <= TXUL_IDLE; + r_busy <= 1'b0; + if ((i_wr)&&(!r_busy)) + begin // Immediately start us off with a start bit + r_busy <= 1'b1; + state <= TXUL_BIT_ZERO; + end + end else begin + // One clock tick in each of these states ... + r_busy <= 1'b1; + if (state <=TXUL_STOP) // start bit, 8-d bits, stop-b + state <= state + 1'b1; + else + state <= TXUL_IDLE; + end + end + // }}} + + // o_busy + // {{{ + // + // This is a wire, designed to be true is we are ever busy above. + // originally, this was going to be true if we were ever not in the + // idle state. The logic has since become more complex, hence we have + // a register dedicated to this and just copy out that registers value. + assign o_busy = (r_busy); + // }}} + + // lcl_data + // {{{ + // + // This is our working copy of the i_data register which we use + // when transmitting. It is only of interest during transmit, and is + // allowed to be whatever at any other time. Hence, if r_busy isn't + // true, we can always set it. On the one clock where r_busy isn't + // true and i_wr is, we set it and r_busy is true thereafter. + // Then, on any zero_baud_counter (i.e. change between baud intervals) + // we simple logically shift the register right to grab the next bit. + initial lcl_data = 8'hff; + always @(posedge i_clk) + if ((i_wr)&&(!r_busy)) + lcl_data <= i_data; + else if (zero_baud_counter) + lcl_data <= { 1'b1, lcl_data[7:1] }; + // }}} + + // o_uart_tx + // {{{ + // + // This is the final result/output desired of this core. It's all + // centered about o_uart_tx. This is what finally needs to follow + // the UART protocol. + // + initial o_uart_tx = 1'b1; + always @(posedge i_clk) + if ((i_wr)&&(!r_busy)) + o_uart_tx <= 1'b0; // Set the start bit on writes + else if (zero_baud_counter) // Set the data bit. + o_uart_tx <= lcl_data[0]; + // }}} + + // Baud counter + // {{{ + // All of the above logic is driven by the baud counter. Bits must last + // CLOCKS_PER_BAUD in length, and this baud counter is what we use to + // make certain of that. + // + // The basic logic is this: at the beginning of a bit interval, start + // the baud counter and set it to count CLOCKS_PER_BAUD. When it gets + // to zero, restart it. + // + // However, comparing a 28'bit number to zero can be rather complex-- + // especially if we wish to do anything else on that same clock. For + // that reason, we create "zero_baud_counter". zero_baud_counter is + // nothing more than a flag that is true anytime baud_counter is zero. + // It's true when the logic (above) needs to step to the next bit. + // Simple enough? + // + // I wish we could stop there, but there are some other (ugly) + // conditions to deal with that offer exceptions to this basic logic. + // + // 1. When the user has commanded a BREAK across the line, we need to + // wait several baud intervals following the break before we start + // transmitting, to give any receiver a chance to recognize that we are + // out of the break condition, and to know that the next bit will be + // a stop bit. + // + // 2. A reset is similar to a break condition--on both we wait several + // baud intervals before allowing a start bit. + // + // 3. In the idle state, we stop our counter--so that upon a request + // to transmit when idle we can start transmitting immediately, rather + // than waiting for the end of the next (fictitious and arbitrary) baud + // interval. + // + // When (i_wr)&&(!r_busy)&&(state == TXUL_IDLE) then we're not only in + // the idle state, but we also just accepted a command to start writing + // the next word. At this point, the baud counter needs to be reset + // to the number of CLOCKS_PER_BAUD, and zero_baud_counter set to zero. + // + // The logic is a bit twisted here, in that it will only check for the + // above condition when zero_baud_counter is false--so as to make + // certain the STOP bit is complete. + initial zero_baud_counter = 1'b1; + initial baud_counter = 0; + always @(posedge i_clk) + begin + zero_baud_counter <= (baud_counter == 1); + if (state == TXUL_IDLE) + begin + baud_counter <= 0; + zero_baud_counter <= 1'b1; + if ((i_wr)&&(!r_busy)) + begin + baud_counter <= CLOCKS_PER_BAUD - 1'b1; + zero_baud_counter <= 1'b0; + end + end else if (!zero_baud_counter) + baud_counter <= baud_counter - 1'b1; + else if (state > TXUL_STOP) + begin + baud_counter <= 0; + zero_baud_counter <= 1'b1; + end else if (state == TXUL_STOP) + // Need to complete this state one clock early, so + // we can release busy one clock before the stop bit + // is complete, so we can start on the next byte + // exactly 10*CLOCKS_PER_BAUD clocks after we started + // the last one + baud_counter <= CLOCKS_PER_BAUD - 2; + else // All other states + baud_counter <= CLOCKS_PER_BAUD - 1'b1; + end + // }}} +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// +// FORMAL METHODS +// {{{ +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +`ifdef FORMAL + // Declarations +`ifdef TXUARTLITE +`define ASSUME assume +`else +`define ASSUME assert +`endif + reg f_past_valid, f_last_clk; + reg [(TB-1):0] f_baud_count; + reg [9:0] f_txbits; + reg [3:0] f_bitcount; + reg [7:0] f_request_tx_data; + wire [3:0] subcount; + + // Setup + // {{{ + initial f_past_valid = 1'b0; + always @(posedge i_clk) + f_past_valid <= 1'b1; + + initial `ASSUME(!i_wr); + always @(posedge i_clk) + if ((f_past_valid)&&($past(i_wr))&&($past(o_busy))) + begin + `ASSUME(i_wr == $past(i_wr)); + `ASSUME(i_data == $past(i_data)); + end + // }}} + + // Check the baud counter + // {{{ + always @(posedge i_clk) + assert(zero_baud_counter == (baud_counter == 0)); + + always @(posedge i_clk) + if ((f_past_valid)&&($past(baud_counter != 0))&&($past(state != TXUL_IDLE))) + assert(baud_counter == $past(baud_counter - 1'b1)); + + always @(posedge i_clk) + if ((f_past_valid)&&(!$past(zero_baud_counter))&&($past(state != TXUL_IDLE))) + assert($stable(o_uart_tx)); + + initial f_baud_count = 1'b0; + always @(posedge i_clk) + if (zero_baud_counter) + f_baud_count <= 0; + else + f_baud_count <= f_baud_count + 1'b1; + + always @(posedge i_clk) + assert(f_baud_count < CLOCKS_PER_BAUD); + + always @(posedge i_clk) + if (baud_counter != 0) + assert(o_busy); + // }}} + + // {{{ + initial f_txbits = 0; + always @(posedge i_clk) + if (zero_baud_counter) + f_txbits <= { o_uart_tx, f_txbits[9:1] }; + + always @(posedge i_clk) + if ((f_past_valid)&&(!$past(zero_baud_counter)) + &&(!$past(state==TXUL_IDLE))) + assert(state == $past(state)); + + initial f_bitcount = 0; + always @(posedge i_clk) + if ((!f_past_valid)||(!$past(f_past_valid))) + f_bitcount <= 0; + else if ((state == TXUL_IDLE)&&(zero_baud_counter)) + f_bitcount <= 0; + else if (zero_baud_counter) + f_bitcount <= f_bitcount + 1'b1; + + always @(posedge i_clk) + assert(f_bitcount <= 4'ha); + + always @(*) + if (!o_busy) + assert(zero_baud_counter); + + always @(posedge i_clk) + if ((i_wr)&&(!o_busy)) + f_request_tx_data <= i_data; + + assign subcount = 10-f_bitcount; + always @(posedge i_clk) + if (f_bitcount > 0) + assert(!f_txbits[subcount]); + + always @(posedge i_clk) + if (f_bitcount == 4'ha) + begin + assert(f_txbits[8:1] == f_request_tx_data); + assert( f_txbits[9]); + end + + always @(posedge i_clk) + assert((state <= TXUL_STOP + 1'b1)||(state == TXUL_IDLE)); + + always @(posedge i_clk) + if ((f_past_valid)&&($past(f_past_valid))&&($past(o_busy))) + cover(!o_busy); + // }}} + +`endif // FORMAL +`ifdef VERIFIC_SVA + reg [7:0] fsv_data; + + // + // Grab a copy of the data any time we are sent a new byte to transmit + // We'll use this in a moment to compare the item transmitted against + // what is supposed to be transmitted + // + always @(posedge i_clk) + if ((i_wr)&&(!o_busy)) + fsv_data <= i_data; + + // + // One baud interval + // {{{ + // + // 1. The UART output is constant at DAT + // 2. The internal state remains constant at ST + // 3. CKS = the number of clocks per bit. + // + // Everything stays constant during the CKS clocks with the exception + // of (zero_baud_counter), which is *only* raised on the last clock + // interval + sequence BAUD_INTERVAL(CKS, DAT, SR, ST); + ((o_uart_tx == DAT)&&(state == ST) + &&(lcl_data == SR) + &&(!zero_baud_counter))[*(CKS-1)] + ##1 (o_uart_tx == DAT)&&(state == ST) + &&(lcl_data == SR) + &&(zero_baud_counter); + endsequence + // }}} + + // + // One byte transmitted + // {{{ + // + // DATA = the byte that is sent + // CKS = the number of clocks per bit + // + sequence SEND(CKS, DATA); + BAUD_INTERVAL(CKS, 1'b0, DATA, 4'h0) + ##1 BAUD_INTERVAL(CKS, DATA[0], {{(1){1'b1}},DATA[7:1]}, 4'h1) + ##1 BAUD_INTERVAL(CKS, DATA[1], {{(2){1'b1}},DATA[7:2]}, 4'h2) + ##1 BAUD_INTERVAL(CKS, DATA[2], {{(3){1'b1}},DATA[7:3]}, 4'h3) + ##1 BAUD_INTERVAL(CKS, DATA[3], {{(4){1'b1}},DATA[7:4]}, 4'h4) + ##1 BAUD_INTERVAL(CKS, DATA[4], {{(5){1'b1}},DATA[7:5]}, 4'h5) + ##1 BAUD_INTERVAL(CKS, DATA[5], {{(6){1'b1}},DATA[7:6]}, 4'h6) + ##1 BAUD_INTERVAL(CKS, DATA[6], {{(7){1'b1}},DATA[7:7]}, 4'h7) + ##1 BAUD_INTERVAL(CKS, DATA[7], 8'hff, 4'h8) + ##1 BAUD_INTERVAL(CKS-1, 1'b1, 8'hff, 4'h9); + endsequence + // }}} + + // + // Transmit one byte + // {{{ + // Once the byte is transmitted, make certain we return to + // idle + // + assert property ( + @(posedge i_clk) + (i_wr)&&(!o_busy) + |=> ((o_busy) throughout SEND(CLOCKS_PER_BAUD,fsv_data)) + ##1 (!o_busy)&&(o_uart_tx)&&(zero_baud_counter)); + // }}} + + // {{{ + assume property ( + @(posedge i_clk) + (i_wr)&&(o_busy) |=> + (i_wr)&&($stable(i_data))); + + // + // Make certain that o_busy is true any time zero_baud_counter is + // non-zero + // + always @(*) + assert((o_busy)||(zero_baud_counter) ); + + // If and only if zero_baud_counter is true, baud_counter must be zero + // Insist on that relationship here. + always @(*) + assert(zero_baud_counter == (baud_counter == 0)); + + // To make certain baud_counter stays below CLOCKS_PER_BAUD + always @(*) + assert(baud_counter < CLOCKS_PER_BAUD); + + // + // Insist that we are only ever in a valid state + always @(*) + assert((state <= TXUL_STOP+1'b1)||(state == TXUL_IDLE)); + // }}} + +`endif // Verific SVA +// }}} +endmodule
diff --git a/verilog/rtl/wbuart32/ufifo.v b/verilog/rtl/wbuart32/ufifo.v new file mode 100644 index 0000000..a50519b --- /dev/null +++ b/verilog/rtl/wbuart32/ufifo.v
@@ -0,0 +1,480 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Filename: ufifo.v +// {{{ +// Project: wbuart32, a full featured UART with simulator +// +// Purpose: A synchronous data FIFO, designed for supporting the Wishbone +// UART. Particular features include the ability to read and +// write on the same clock, while maintaining the correct output FIFO +// parameters. Two versions of the FIFO exist within this file, separated +// by the RXFIFO parameter's value. One, where RXFIFO = 1, produces status +// values appropriate for reading and checking a read FIFO from logic, +// whereas the RXFIFO = 0 applies to writing to the FIFO from bus logic +// and reading it automatically any time the transmit UART is idle. +// +// Creator: Dan Gisselquist, Ph.D. +// Gisselquist Technology, LLC +// +//////////////////////////////////////////////////////////////////////////////// +// }}} +// Copyright (C) 2015-2021, Gisselquist Technology, LLC +// {{{ +// This program is free software (firmware): you can redistribute it and/or +// modify it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program. (It's in the $(ROOT)/doc directory. Run make with no +// target there if the PDF file isn't present.) If not, see +// <http://www.gnu.org/licenses/> for a copy. +// }}} +// License: GPL, v3, as defined and found on www.gnu.org, +// {{{ +// http://www.gnu.org/licenses/gpl.html +// +// +//////////////////////////////////////////////////////////////////////////////// +// +// +//`default_nettype none +// }}} +module ufifo #( + // {{{ + parameter BW=8, // Byte/data width + parameter [3:0] LGFLEN=4, + parameter [0:0] RXFIFO=1'b1 + + // }}} + ) ( + // {{{ + input wire i_clk, i_reset, + input wire i_wr, + input wire [(BW-1):0] i_data, + output wire o_empty_n, // True if something is in FIFO + input wire i_rd, + output wire [(BW-1):0] o_data, + output wire [15:0] o_status, + output wire o_err + // }}} + ); + + localparam FLEN=(1<<LGFLEN); + + // Signal declarations + // {{{ + reg [(BW-1):0] fifo[0:(FLEN-1)]; + reg [(BW-1):0] r_data, last_write; + reg [(LGFLEN-1):0] wr_addr, rd_addr, r_next; + reg will_overflow, will_underflow; + reg osrc; + + wire [(LGFLEN-1):0] w_waddr_plus_one, w_waddr_plus_two; + wire w_write, w_read; + reg [(LGFLEN-1):0] r_fill; + wire [3:0] lglen; + wire w_half_full; + reg [9:0] w_fill; + // }}} + + assign w_write = (i_wr && (!will_overflow || i_rd)); + assign w_read = (i_rd && o_empty_n); + + assign w_waddr_plus_two = wr_addr + 2; + assign w_waddr_plus_one = wr_addr + 1; + + //////////////////////////////////////////////////////////////////////// + // + // Write half + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + + // will_overflow + // {{{ + initial will_overflow = 1'b0; + always @(posedge i_clk) + if (i_reset) + will_overflow <= 1'b0; + else if (i_rd) + will_overflow <= (will_overflow)&&(i_wr); + else if (w_write) + will_overflow <= (will_overflow)||(w_waddr_plus_two == rd_addr); + else if (w_waddr_plus_one == rd_addr) + will_overflow <= 1'b1; + // }}} + + // wr_addr + // {{{ + initial wr_addr = 0; + always @(posedge i_clk) + if (i_reset) + wr_addr <= { (LGFLEN){1'b0} }; + else if (w_write) + wr_addr <= w_waddr_plus_one; + // }}} + + // Write to the FIFO + // {{{ + always @(posedge i_clk) + if (w_write) // Write our new value regardless--on overflow or not + fifo[wr_addr] <= i_data; + // }}} + // }}} + //////////////////////////////////////////////////////////////////////// + // + // Read half + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + + // Notes + // {{{ + // Following a read, the next sample will be available on the + // next clock + // Clock ReadCMD ReadAddr Output + // 0 0 0 fifo[0] + // 1 1 0 fifo[0] + // 2 0 1 fifo[1] + // 3 0 1 fifo[1] + // 4 1 1 fifo[1] + // 5 1 2 fifo[2] + // 6 0 3 fifo[3] + // 7 0 3 fifo[3] + // }}} + + // will_underflow + // {{{ + initial will_underflow = 1'b1; + always @(posedge i_clk) + if (i_reset) + will_underflow <= 1'b1; + else if (i_wr) + will_underflow <= 1'b0; + else if (w_read) + will_underflow <= (will_underflow)||(r_next == wr_addr); + // }}} + + // rd_addr, r_next + // {{{ + // Don't report FIFO underflow errors. These'll be caught elsewhere + // in the system, and the logic below makes it hard to reset them. + // We'll still report FIFO overflow, however. + // + initial rd_addr = 0; + initial r_next = 1; + always @(posedge i_clk) + if (i_reset) + begin + rd_addr <= 0; + r_next <= 1; + end else if (w_read) + begin + rd_addr <= rd_addr + 1; + r_next <= rd_addr + 2; + end + // }}} + + // Read from the FIFO + // {{{ + always @(posedge i_clk) + if (w_read) + r_data <= fifo[r_next[LGFLEN-1:0]]; + // }}} + + // last_write -- for bypassing the memory read + // {{{ + always @(posedge i_clk) + if (i_wr && (!o_empty_n || (w_read && r_next == wr_addr))) + last_write <= i_data; + // }}} + + // osrc + // {{{ + initial osrc = 1'b0; + always @(posedge i_clk) + if (i_reset) + osrc <= 1'b0; + else if (i_wr && (!o_empty_n || (w_read && r_next == wr_addr))) + osrc <= 1'b1; + else if (i_rd) + osrc <= 1'b0; + // }}} + + assign o_data = (osrc) ? last_write : r_data; + // }}} + //////////////////////////////////////////////////////////////////////// + // + // Status signals and flags + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + + // r_fill + // {{{ + // If this is a receive FIFO, the FIFO count that matters is the number + // of values yet to be read. If instead this is a transmit FIFO, then + // the FIFO count that matters is the number of empty positions that + // can still be filled before the FIFO is full. + // + // Adjust for these differences here. + generate if (RXFIFO) + begin : RXFIFO_FILL + // {{{ + // Calculate the number of elements in our FIFO + // + // Although used for receive, this is actually the more + // generic answer--should you wish to use the FIFO in + // another context. + + initial r_fill = 0; + always @(posedge i_clk) + if (i_reset) + r_fill <= 0; + else case({ w_write, w_read }) + 2'b01: r_fill <= r_fill - 1'b1; + 2'b10: r_fill <= r_fill + 1'b1; + default: begin end + endcase + // }}} + end else begin : TXFIFO_FILL + // {{{ + // Calculate the number of empty elements in our FIFO + // + // This is the number you could send to the FIFO + // if you wanted to. + + initial r_fill = -1; + always @(posedge i_clk) + if (i_reset) + r_fill <= -1; + else case({ w_write, w_read }) + 2'b01: r_fill <= r_fill + 1'b1; + 2'b10: r_fill <= r_fill - 1'b1; + default: begin end + endcase + // }}} + end endgenerate + // }}} + + // o_err -- Flag any overflows + // {{{ + assign o_err = (i_wr && !w_write); + // }}} + + // o_status + // {{{ + assign lglen = LGFLEN; + + always @(*) + begin + w_fill = 0; + w_fill[(LGFLEN-1):0] = r_fill; + end + + assign w_half_full = r_fill[(LGFLEN-1)]; + + assign o_status = { + // Our status includes a 4'bit nibble telling anyone reading + // this the size of our FIFO. The size is then given by + // 2^(this value). Hence a 4'h4 in this position means that the + // FIFO has 2^4 or 16 values within it. + lglen, + // The FIFO fill--for a receive FIFO the number of elements + // left to be read, and for a transmit FIFO the number of + // empty elements within the FIFO that can yet be filled. + w_fill, + // A '1' here means a half FIFO length can be read (receive + // FIFO) or written to (not a receive FIFO). If one, a + // halfway interrupt can be sent indicating a half of a FIFOs + // operationw (either transmit or receive) will be successful. + w_half_full, + // A '1' here means the FIFO can be read from (if it is a + // receive FIFO), or be written to (if it isn't). An interrupt + // may be sourced from this bit, indicating that at least one + // operation will be successful. + (RXFIFO!=0)?!will_underflow:!will_overflow + }; + // }}} + + assign o_empty_n = !will_underflow; + // }}} +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// +// Formal property section +// {{{ +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +`ifdef FORMAL + reg f_past_valid; + + initial f_past_valid = 0; + always @(posedge i_clk) + f_past_valid <= 1; + + //////////////////////////////////////////////////////////////////////// + // + // Pointer checks + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + reg [LGFLEN-1:0] f_fill; + wire [LGFLEN-1:0] f_raddr_plus_one; + + always @(*) + f_fill = wr_addr - rd_addr; + + always @(*) + assert(will_underflow == (f_fill == 0)); + + always @(*) + assert(will_overflow == (&f_fill)); + + assign f_raddr_plus_one = rd_addr + 1; + + always @(*) + assert(f_raddr_plus_one == r_next); + + always @(*) + if (will_underflow) + begin + assert(!w_read); + assert(!osrc); + end + + + always @(posedge i_clk) + if (RXFIFO) + assert(r_fill == f_fill); + else + assert(r_fill == (~f_fill)); + // }}} + //////////////////////////////////////////////////////////////////////// + // + // Twin write check + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // +`ifdef UFIFO + // Declare two arbitrary addresses and data values + // {{{ + (* anyconst *) reg [LGFLEN-1:0] f_const_addr; + (* anyconst *) reg [BW-1:0] f_const_data, f_const_second; + reg [LGFLEN-1:0] f_next_addr; + reg [1:0] f_state; + reg f_first_in_fifo, f_second_in_fifo; + reg [LGFLEN-1:0] f_distance_to_first, f_distance_to_second; + // }}} + + // Determine if those data values are at their addresses in the FIFO + // {{{ + always @(*) + begin + f_next_addr = f_const_addr + 1; + + f_distance_to_first = f_const_addr - rd_addr; + f_distance_to_second = f_next_addr - rd_addr; + + f_first_in_fifo = (f_distance_to_first < f_fill) + && !will_underflow + && (fifo[f_const_addr] == f_const_data); + f_second_in_fifo = (f_distance_to_second < f_fill) + && !will_underflow + && (fifo[f_next_addr] == f_const_second); + end + // }}} + + // Generate the twin-write state machine + // {{{ + initial f_state = 2'b00; + always @(posedge i_clk) + if (i_reset) + f_state <= 2'b00; + else case(f_state) + 2'b00: if (w_write &&(wr_addr == f_const_addr) + &&(i_data == f_const_data)) + f_state <= 2'b01; + 2'b01: if (w_read && (rd_addr == f_const_addr)) + f_state <= 2'b00; + else if (w_write && (wr_addr == f_next_addr)) + f_state <= (i_data == f_const_second) ? 2'b10 : 2'b00; + 2'b10: if (w_read && (rd_addr == f_const_addr)) + f_state <= 2'b11; + 2'b11: if (w_read) + f_state <= 2'b00; + endcase + // }}} + + // Check conditions against the twin write state machine + // {{{ + always @(*) + case(f_state) + 2'b00: begin end + 2'b01: begin + assert(!will_underflow); + assert(f_first_in_fifo); + assert(!f_second_in_fifo); + assert(wr_addr == f_next_addr); + assert(fifo[f_const_addr] == f_const_data); + if (rd_addr == f_const_addr) + assert(o_data == f_const_data); + end + 2'b10: begin + assert(f_first_in_fifo); + assert(f_second_in_fifo); + end + 2'b11: begin + assert(f_second_in_fifo); + assert(rd_addr == f_next_addr); + assert(o_data == f_const_second); + end + endcase + // }}} +`endif + // }}} + //////////////////////////////////////////////////////////////////////// + // + // Cover checks + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + reg cvr_filled; + + always @(*) + cover(o_empty_n); + + // Can't cover the FIFO being full when the FIFO is a member of another + // components--so we only check that we can be filled here +`ifdef UFIFO + always @(*) + cover(o_err); + + initial cvr_filled = 0; + always @(posedge i_clk) + if (i_reset) + cvr_filled <= 0; + else if (&f_fill[LGFLEN-1:0]) + cvr_filled <= 1; + + always @(*) + cover(cvr_filled && !o_empty_n); +`endif // UFIFO + // }}} +`endif +// }}} +endmodule
diff --git a/verilog/rtl/wbuart32/wbuart-insert.v b/verilog/rtl/wbuart32/wbuart-insert.v new file mode 100644 index 0000000..cfdcbac --- /dev/null +++ b/verilog/rtl/wbuart32/wbuart-insert.v
@@ -0,0 +1,147 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Filename: wbuart-insert.v +// {{{ +// Project: wbuart32, a full featured UART with simulator +// +// Purpose: This is not a module file. It is an example of the types of +// lines and connections which can be used to connect this UART +// to a local wishbone bus. It was drawn from a working file, and +// modified here for show, so ... let me know if I messed anything up +// along the way. +// +// Why isn't this a full module file? Because I tend to lump all of my +// single cycle I/O peripherals into one module file. It makes the logic +// simpler. This particular file was extracted from the fastio.v file +// within the openarty project. +// +// Creator: Dan Gisselquist, Ph.D. +// Gisselquist Technology, LLC +// +//////////////////////////////////////////////////////////////////////////////// +// }}} +// Copyright (C) 2015-2021, Gisselquist Technology, LLC +// {{{ +// This program is free software (firmware): you can redistribute it and/or +// modify it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program. (It's in the $(ROOT)/doc directory, run make with no +// target there if the PDF file isn't present.) If not, see +// <http://www.gnu.org/licenses/> for a copy. +// +// License: GPL, v3, as defined and found on www.gnu.org, +// http://www.gnu.org/licenses/gpl.html +// +// +//////////////////////////////////////////////////////////////////////////////// +// +// }}} + + + // Ideally, UART_SETUP is defined somewhere. I commonly like to define + // it to CLKRATE / BAUDRATE, to give me 8N1 performance. 4MB is useful + // to me, so 100MHz / 4M = 25 could be the setup. You can also use + // 200MHz / 4MB = 50 ... it all depends upon your clock. +`define UART_SETUP 31'd25 + reg [30:0] uart_setup; + initial uart_setup = `UART_SETUP; + always @(posedge i_clk) + if ((i_wb_stb)&&(i_wb_addr == `UART_SETUP_ADDR)) + uart_setup[30:0] <= i_wb_data[30:0]; + + // + // First the UART receiver + // + wire rx_stb, rx_break, rx_perr, rx_ferr, ck_uart; + wire [7:0] rx_data_port; + rxuart #(UART_SETUP) rx(i_clk, 1'b0, uart_setup, i_rx, + rx_stb, rx_data_port, rx_break, + rx_perr, rx_ferr, ck_uart); + + wire [31:0] rx_data; + reg [11:0] r_rx_data; + always @(posedge i_clk) + if (rx_stb) + begin + r_rx_data[11] <= (r_rx_data[11])||(rx_break); + r_rx_data[10] <= (r_rx_data[10])||(rx_ferr); + r_rx_data[ 9] <= (r_rx_data[ 9])||(rx_perr); + r_rx_data[7:0]<= rx_data_port; + end else if ((i_wb_stb)&&(i_wb_we) + &&(i_wb_addr == `UART_RX_ADDR)) + begin + r_rx_data[11] <= (rx_break)&& (!i_wb_data[11]); + r_rx_data[10] <= (rx_ferr) && (!i_wb_data[10]); + r_rx_data[ 9] <= (rx_perr) && (!i_wb_data[ 9]); + end + always @(posedge i_clk) + if(((i_wb_stb)&&(!i_wb_we)&&(i_wb_addr == `UART_RX_ADDR)) + ||(rx_stb)) + r_rx_data[8] <= !rx_stb; + assign o_rts_n = r_rx_data[8]; + assign rx_data = { 20'h00, r_rx_data }; + assign rx_int = !r_rx_data[8]; + + // Transmit hardware flow control, the cts line + wire cts_n; + // Set this cts value to zero if you aren't ever going to use H/W flow + // control, otherwise set it to the value coming in from the external + // i_cts_n pin. + assign cts_n = i_cts_n; + + // + // Then the UART transmitter + // + // + // + // Now onto the transmitter itself + wire tx_busy; + reg [7:0] r_tx_data; + reg r_tx_stb, r_tx_break; + wire [31:0] tx_data; + txuart #(UART_SETUP) tx(i_clk, 1'b0, uart_setup, + r_tx_break, r_tx_stb, r_tx_data, + cts_n, o_tx, tx_busy); + always @(posedge i_clk) + if ((i_wb_stb)&&(i_wb_addr == 5'h0f)) + begin + r_tx_stb <= (!r_tx_break)&&(!i_wb_data[8]); + r_tx_data <= i_wb_data[7:0]; + r_tx_break<= i_wb_data[9]; + end else if (!tx_busy) + begin + r_tx_stb <= 1'b0; + r_tx_data <= 8'h0; + end + assign tx_data = { 16'h00, cts_n, 3'h0, + ck_uart, o_tx, r_tx_break, tx_busy, + r_tx_data }; + assign tx_int = ~tx_busy; + + always @(posedge i_clk) + case(i_wb_addr) + `UART_SETUP_ADDR: o_wb_data <= { 1'b0, uart_setup }; + `UART_RX_ADDR : o_wb_data <= rx_data; + `UART_TX_ADDR : o_wb_data <= tx_data; + // + // The rest of these address slots are left open here for + // whatever else you might wish to connect to this bus/STB + // line + default: o_wb_data <= 32'h00; + endcase + + assign o_wb_stall = 1'b0; + always @(posedge i_clk) + o_wb_ack <= (i_wb_stb); + + // Interrupts sent to the board from here + assign o_board_ints = { rx_int, tx_int /* any other from this module */}; +
diff --git a/verilog/rtl/wbuart32/wbuart.v b/verilog/rtl/wbuart32/wbuart.v new file mode 100644 index 0000000..c37e86b --- /dev/null +++ b/verilog/rtl/wbuart32/wbuart.v
@@ -0,0 +1,525 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Filename: wbuart.v +// {{{ +// Project: wbuart32, a full featured UART with simulator +// +// Purpose: Unlilke wbuart-insert.v, this is a full blown wishbone core +// with integrated FIFO support to support the UART transmitter +// and receiver found within here. As a result, it's usage may be +// heavier on the bus than the insert, but it may also be more useful. +// +// Creator: Dan Gisselquist, Ph.D. +// Gisselquist Technology, LLC +// +//////////////////////////////////////////////////////////////////////////////// +// }}} +// Copyright (C) 2015-2021, Gisselquist Technology, LLC +// {{{ +// This program is free software (firmware): you can redistribute it and/or +// modify it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program. (It's in the $(ROOT)/doc directory. Run make with no +// target there if the PDF file isn't present.) If not, see +// <http://www.gnu.org/licenses/> for a copy. +// }}} +// License: GPL, v3, as defined and found on www.gnu.org, +// {{{ +// http://www.gnu.org/licenses/gpl.html +// +// +//////////////////////////////////////////////////////////////////////////////// +// +// +//`default_nettype none +// }}} +`define USE_LITE_UART +module wbuart #( + // {{{ + // 4MB 8N1, when using 100MHz clock + parameter [30:0] INITIAL_SETUP = 31'd25, + parameter [3:0] LGFLEN = 4, + parameter [0:0] HARDWARE_FLOW_CONTROL_PRESENT = 1'b1 + // Perform a simple/quick bounds check on the log FIFO length, + // to make sure its within the bounds we can support with our + // current interface. + + // }}} + ) ( + // {{{ + input wire i_clk, i_reset, + // Wishbone inputs + input wire i_wb_cyc, + input wire i_wb_stb, i_wb_we, + input wire [1:0] i_wb_addr, + input wire [31:0] i_wb_data, + input wire [3:0] i_wb_sel, + output wire o_wb_stall, + output reg o_wb_ack, + output reg [31:0] o_wb_data, + // + input wire i_uart_rx, + output wire o_uart_tx, + input wire i_cts_n, + output reg o_rts_n, + output wire o_uart_rx_int, o_uart_tx_int, + o_uart_rxfifo_int, o_uart_txfifo_int + // }}} + ); + + localparam [3:0] LCLLGFLEN = (LGFLEN > 4'ha)? 4'ha + : ((LGFLEN < 4'h2) ? 4'h2 : LGFLEN); + + localparam [1:0] UART_SETUP = 2'b00, + UART_FIFO = 2'b01, + UART_RXREG = 2'b10, + UART_TXREG = 2'b11; + + // Register and signal declarations + // {{{ + wire tx_busy; + reg [30:0] uart_setup; + // Receiver + wire rx_stb, rx_break, rx_perr, rx_ferr, ck_uart; + wire [7:0] rx_uart_data; + reg rx_uart_reset; + // Receive FIFO + wire rx_empty_n, rx_fifo_err; + wire [7:0] rxf_wb_data; + wire [15:0] rxf_status; + reg rxf_wb_read; + // + wire [(LCLLGFLEN-1):0] check_cutoff; + reg r_rx_perr, r_rx_ferr; + wire [31:0] wb_rx_data; + // The transmitter + wire tx_empty_n, txf_err, tx_break; + wire [7:0] tx_data; + wire [15:0] txf_status; + reg txf_wb_write, tx_uart_reset; + reg [7:0] txf_wb_data; + // + wire [31:0] wb_tx_data; + wire [31:0] wb_fifo_data; + reg [1:0] r_wb_addr; + reg r_wb_ack; + // }}} + + // uart_setup + // {{{ + // The UART setup parameters: bits per byte, stop bits, parity, and + // baud rate are all captured within this uart_setup register. + // + initial uart_setup = INITIAL_SETUP + | ((HARDWARE_FLOW_CONTROL_PRESENT==1'b0)? 31'h40000000 : 0); + always @(posedge i_clk) + // Under wishbone rules, a write takes place any time i_wb_stb + // is high. If that's the case, and if the write was to the + // setup address, then set us up for the new parameters. + if ((i_wb_stb)&&(i_wb_addr == UART_SETUP)&&(i_wb_we)) + begin + if (i_wb_sel[0]) + uart_setup[7:0] <= i_wb_data[7:0]; + if (i_wb_sel[1]) + uart_setup[15:8] <= i_wb_data[15:8]; + if (i_wb_sel[2]) + uart_setup[23:16] <= i_wb_data[23:16]; + if (i_wb_sel[3]) + uart_setup[30:24] <= { (i_wb_data[30]) + ||(!HARDWARE_FLOW_CONTROL_PRESENT), + i_wb_data[29:24] }; + end + // }}} + //////////////////////////////////////////////////////////////////////// + // + // The UART receiver + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + + // The receiver itself + // {{{ + // Here's our UART receiver. Basically, it accepts our setup wires, + // the UART input, a clock, and a reset line, and produces outputs: + // a stb (true when new data is ready), and an 8-bit data out value + // valid when stb is high. +`ifdef USE_LITE_UART + // {{{ + rxuartlite #(.CLOCKS_PER_BAUD(INITIAL_SETUP[23:0])) + rx(i_clk, i_uart_rx, rx_stb, rx_uart_data); + assign rx_break = 1'b0; + assign rx_perr = 1'b0; + assign rx_ferr = 1'b0; + assign ck_uart = 1'b0; + // }}} +`else + // {{{ + // The full receiver also produces a break value (true during a break + // cond.), and parity/framing error flags--also valid when stb is true. + rxuart #(.INITIAL_SETUP(INITIAL_SETUP)) rx(i_clk, (i_reset)||(rx_uart_reset), + uart_setup, i_uart_rx, + rx_stb, rx_uart_data, rx_break, + rx_perr, rx_ferr, ck_uart); + // The real trick is ... now that we have this extra data, what do we do + // with it? + // }}} +`endif + // }}} + + // The receive FIFO + // {{{ + // We place new arriving data into a receiver FIFO. + // + // And here's the FIFO proper. + // + // Note that the FIFO will be cleared upon any reset: either if there's + // a UART break condition on the line, the receiver is in reset, or an + // external reset is issued. + // + // The FIFO accepts strobe and data from the receiver. + // We issue another wire to it (rxf_wb_read), true when we wish to read + // from the FIFO, and we get our data in rxf_wb_data. The FIFO outputs + // four status-type values: 1) is it non-empty, 2) is the FIFO over half + // full, 3) a 16-bit status register, containing info regarding how full + // the FIFO truly is, and 4) an error indicator. + ufifo #(.LGFLEN(LCLLGFLEN), .RXFIFO(1)) + rxfifo(i_clk, (i_reset)||(rx_break)||(rx_uart_reset), + rx_stb, rx_uart_data, + rx_empty_n, + rxf_wb_read, rxf_wb_data, + rxf_status, rx_fifo_err); + // }}} + + assign o_uart_rxfifo_int = rxf_status[1]; + + // We produce four interrupts. One of the receive interrupts indicates + // whether or not the receive FIFO is non-empty. This should wake up + // the CPU. + assign o_uart_rx_int = rxf_status[0]; + + // o_rts_n + // {{{ + // The clear to send line, which may be ignored, but which we set here + // to be true any time the FIFO has fewer than N-2 items in it. + // Why not N-1? Because at N-1 we are totally full, but already so full + // that if the transmit end starts sending we won't have a location to + // receive it. (Transmit might've started on the next character by the + // time we set this--thus we need to set it to one, one character before + // necessary). + assign check_cutoff = -3; + always @(posedge i_clk) + o_rts_n <= ((HARDWARE_FLOW_CONTROL_PRESENT) + &&(!uart_setup[30]) + &&(rxf_status[(LCLLGFLEN+1):2] > check_cutoff)); + // }}} + + // rxf_wb_read + // {{{ + // If the bus requests that we read from the receive FIFO, we need to + // tell this to the receive FIFO. Note that because we are using a + // clock here, the output from the receive FIFO will necessarily be + // delayed by an extra clock. + initial rxf_wb_read = 1'b0; + always @(posedge i_clk) + rxf_wb_read <= (i_wb_stb)&&(i_wb_addr[1:0]== UART_RXREG) + &&(!i_wb_we); + // }}} + + // r_rx_perr, r_rx_ferr -- parity and framing errors + // {{{ + // Now, let's deal with those RX UART errors: both the parity and frame + // errors. As you may recall, these are valid only when rx_stb is + // valid, so we need to hold on to them until the user reads them via + // a UART read request.. + initial r_rx_perr = 1'b0; + initial r_rx_ferr = 1'b0; + always @(posedge i_clk) + if ((rx_uart_reset)||(rx_break)) + begin + // Clear the error + r_rx_perr <= 1'b0; + r_rx_ferr <= 1'b0; + end else if ((i_wb_stb) + &&(i_wb_addr[1:0]== UART_RXREG)&&(i_wb_we)) + begin + // Reset the error lines if a '1' is ever written to + // them, otherwise leave them alone. + // + if (i_wb_sel[1]) + begin + r_rx_perr <= (r_rx_perr)&&(~i_wb_data[9]); + r_rx_ferr <= (r_rx_ferr)&&(~i_wb_data[10]); + end + end else if (rx_stb) + begin + // On an rx_stb, capture any parity or framing error + // indications. These aren't kept with the data rcvd, + // but rather kept external to the FIFO. As a result, + // if you get a parity or framing error, you will never + // know which data byte it was associated with. + // For now ... that'll work. + r_rx_perr <= (r_rx_perr)||(rx_perr); + r_rx_ferr <= (r_rx_ferr)||(rx_ferr); + end + // }}} + + // rx_uart_reset + // {{{ + initial rx_uart_reset = 1'b1; + always @(posedge i_clk) + if ((i_reset)||((i_wb_stb)&&(i_wb_addr[1:0]== UART_SETUP)&&(i_wb_we))) + // The receiver reset, always set on a master reset + // request. + rx_uart_reset <= 1'b1; + else if ((i_wb_stb)&&(i_wb_addr[1:0]== UART_RXREG)&&(i_wb_we)&&i_wb_sel[1]) + // Writes to the receive register will command a receive + // reset anytime bit[12] is set. + rx_uart_reset <= i_wb_data[12]; + else + rx_uart_reset <= 1'b0; + // }}} + + // wb_rx_data + // {{{ + // Finally, we'll construct a 32-bit value from these various wires, + // to be returned over the bus on any read. These include the data + // that would be read from the FIFO, an error indicator set upon + // reading from an empty FIFO, a break indicator, and the frame and + // parity error signals. + assign wb_rx_data = { 16'h00, + 3'h0, rx_fifo_err, + rx_break, rx_ferr, r_rx_perr, !rx_empty_n, + rxf_wb_data}; + // }}} + // }}} + //////////////////////////////////////////////////////////////////////// + // + // The UART transmitter + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + + // txf_wb_write, txf_wb_data + // {{{ + // Unlike the receiver which goes from RXUART -> UFIFO -> WB, the + // transmitter basically goes WB -> UFIFO -> TXUART. Hence, to build + // support for the transmitter, we start with the command to write data + // into the FIFO. In this case, we use the act of writing to the + // UART_TXREG address as our indication that we wish to write to the + // FIFO. Here, we create a write command line, and latch the data for + // the extra clock that it'll take so that the command and data can be + // both true on the same clock. + initial txf_wb_write = 1'b0; + always @(posedge i_clk) + begin + txf_wb_write <= (i_wb_stb)&&(i_wb_addr == UART_TXREG) + &&(i_wb_we)&&(i_wb_sel[0]); + txf_wb_data <= i_wb_data[7:0]; + end + // }}} + + // Transmit FIFO + // {{{ + // Most of this is just wire management. The TX FIFO is identical in + // implementation to the RX FIFO (theyre both UFIFOs), but the TX + // FIFO is fed from the WB and read by the transmitter. Some key + // differences to note: we reset the transmitter on any request for a + // break. We read from the FIFO any time the UART transmitter is idle. + // and ... we just set the values (above) for controlling writing into + // this. + ufifo #(.LGFLEN(LGFLEN), .RXFIFO(0)) + txfifo(i_clk, (tx_break)||(tx_uart_reset), + txf_wb_write, txf_wb_data, + tx_empty_n, + (!tx_busy)&&(tx_empty_n), tx_data, + txf_status, txf_err); + // }}} + + // Transmit interrupts + // {{{ + // Let's create two transmit based interrupts from the FIFO for the CPU. + // The first will be true any time the FIFO has at least one open + // position within it. + assign o_uart_tx_int = txf_status[0]; + // The second will be true any time the FIFO is less than half + // full, allowing us a change to always keep it (near) fully + // charged. + assign o_uart_txfifo_int = txf_status[1]; + // }}} + + // Break logic +`ifndef USE_LITE_UART + // {{{ + // A break in a UART controller is any time the UART holds the line + // low for an extended period of time. Here, we capture the wb_data[9] + // wire, on writes, as an indication we wish to break. As long as you + // write unsigned characters to the interface, this will never be true + // unless you wish it to be true. Be aware, though, writing a valid + // value to the interface will bring it out of the break condition. + reg r_tx_break; + initial r_tx_break = 1'b0; + always @(posedge i_clk) + if (i_reset) + r_tx_break <= 1'b0; + else if ((i_wb_stb)&&(i_wb_addr[1:0]== UART_TXREG)&&(i_wb_we) + &&(i_wb_sel[1])) + r_tx_break <= i_wb_data[9]; + + assign tx_break = r_tx_break; + // }}} +`else + // {{{ + assign tx_break = 1'b0; + // }}} +`endif + + // TX-Reset logic + // {{{{ + // This is nearly identical to the RX reset logic above. Basically, + // any time someone writes to bit [12] the transmitter will go through + // a reset cycle. Keep bit [12] low, and everything will proceed as + // normal. + initial tx_uart_reset = 1'b1; + always @(posedge i_clk) + if((i_reset)||((i_wb_stb)&&(i_wb_addr == UART_SETUP)&&(i_wb_we))) + tx_uart_reset <= 1'b1; + else if ((i_wb_stb)&&(i_wb_addr[1:0]== UART_TXREG)&&(i_wb_we) && i_wb_sel[1]) + tx_uart_reset <= i_wb_data[12]; + else + tx_uart_reset <= 1'b0; + // }}} + + // The actuall transmitter itself +`ifdef USE_LITE_UART + // {{{ + txuartlite #(.CLOCKS_PER_BAUD(INITIAL_SETUP[23:0])) tx(i_clk, (tx_empty_n), tx_data, + o_uart_tx, tx_busy); + // }}} +`else + // cts_n + // {{{ + wire cts_n; + assign cts_n = (HARDWARE_FLOW_CONTROL_PRESENT)&&(i_cts_n); + // }}} + + // The *full* transmitter impleemntation + // {{{ + // Finally, the UART transmitter module itself. Note that we haven't + // connected the reset wire. Transmitting is as simple as setting + // the stb value (here set to tx_empty_n) and the data. When these + // are both set on the same clock that tx_busy is low, the transmitter + // will move on to the next data byte. Really, the only thing magical + // here is that tx_empty_n wire--thus, if there's anything in the FIFO, + // we read it here. (You might notice above, we register a read any + // time (tx_empty_n) and (!tx_busy) are both true---the condition for + // starting to transmit a new byte.) + txuart #(.INITIAL_SETUP(INITIAL_SETUP)) tx(i_clk, 1'b0, uart_setup, + r_tx_break, (tx_empty_n), tx_data, + cts_n, o_uart_tx, tx_busy); + // }}} +`endif + + // wb_tx_data + // {{{ + // Now that we are done with the chain, pick some wires for the user + // to read on any read of the transmit port. + // + // This port is different from reading from the receive port, since + // there are no side effects. (Reading from the receive port advances + // the receive FIFO, here only writing to the transmit port advances the + // transmit FIFO--hence the read values are free for ... whatever.) + // We choose here to provide information about the transmit FIFO + // (txf_err, txf_half_full, txf_full_n), information about the current + // voltage on the line (o_uart_tx)--and even the voltage on the receive + // line (ck_uart), as well as our current setting of the break and + // whether or not we are actively transmitting. + assign wb_tx_data = { 16'h00, + i_cts_n, txf_status[1:0], txf_err, + ck_uart, o_uart_tx, tx_break, (tx_busy|txf_status[0]), + (tx_busy|txf_status[0])?txf_wb_data:8'b00}; + // }}} + // }}} + //////////////////////////////////////////////////////////////////////// + // + // Bus / register handling + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + + + // wb_fifo_data + // {{{ + // Each of the FIFO's returns a 16 bit status value. This value tells + // us both how big the FIFO is, as well as how much of the FIFO is in + // use. Let's merge those two status words together into a word we + // can use when reading about the FIFO. + assign wb_fifo_data = { txf_status, rxf_status }; + // }}} + + // r_wb_addr + // {{{ + // You may recall from above that reads take two clocks. Hence, we + // need to delay the address decoding for a clock until the data is + // ready. We do that here. + always @(posedge i_clk) + r_wb_addr <= i_wb_addr; + // }}} + + // r_wb_ack + // {{{ + initial r_wb_ack = 1'b0; + always @(posedge i_clk) // We'll ACK in two clocks + r_wb_ack <= i_wb_stb; + // }}} + + // o_wb_ack + // {{{ + initial o_wb_ack = 1'b0; + always @(posedge i_clk) // Okay, time to set the ACK + o_wb_ack <= i_wb_cyc && r_wb_ack; + // }}} + + // o_wb_data + // {{{ + // Finally, set the return data. This data must be valid on the same + // clock o_wb_ack is high. On all other clocks, it is irrelelant--since + // no one cares, no one is reading it, it gets lost in the mux in the + // interconnect, etc. For this reason, we can just simplify our logic. + always @(posedge i_clk) + casez(r_wb_addr) + UART_SETUP: o_wb_data <= { 1'b0, uart_setup }; + UART_FIFO: o_wb_data <= wb_fifo_data; + UART_RXREG: o_wb_data <= wb_rx_data; + UART_TXREG: o_wb_data <= wb_tx_data; + endcase + // }}} + + // o_wb_stall + // {{{ + // This device never stalls. Sure, it takes two clocks, but they are + // pipelined, and nothing stalls that pipeline. (Creates FIFO errors, + // perhaps, but doesn't stall the pipeline.) Hence, we can just + // set this value to zero. + assign o_wb_stall = 1'b0; + // }}} + // }}} + + // Make verilator happy + // {{{ + // verilator lint_off UNUSED + wire unused; + assign unused = &{ 1'b0, i_wb_data[31] }; + // verilator lint_on UNUSED + // }}} +endmodule