diff --git a/modules/dem_uart/common/fifo_singleclock_fwft.sv b/modules/dem_uart/common/fifo_singleclock_fwft.sv new file mode 100644 index 0000000..03b64ae --- /dev/null +++ b/modules/dem_uart/common/fifo_singleclock_fwft.sv @@ -0,0 +1,116 @@ +/* Copyright (c) 2017 by the author(s) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * ============================================================================= + * + * Synchronous First-Word Fall-Through (FWFT) FIFO + * + * This FIFO implementation wraps the FIFO with standard read characteristics + * to have first-word fall-through read characteristics. + * + * Author(s): + * Philipp Wagner + */ + +module fifo_singleclock_fwft #( + parameter WIDTH = 8, + parameter DEPTH = 32, + parameter PROG_FULL = (DEPTH / 2) +)( + input clk, + input rst, + + input [(WIDTH-1):0] din, + input wr_en, + output full, + output prog_full, + + output reg [(WIDTH-1):0] dout, + input rd_en, + output empty, + + output [$clog2(DEPTH)-1:0] count +); + + reg fifo_valid, middle_valid, dout_valid; + reg [(WIDTH-1):0] middle_dout; + + wire [(WIDTH-1):0] fifo_dout; + wire fifo_empty, fifo_rd_en; + wire will_update_middle, will_update_dout; + + // synchronous FIFO with standard (non-FWFT) read characteristics + fifo_singleclock_standard + #(.WIDTH(WIDTH), + .DEPTH(DEPTH), + .PROG_FULL(PROG_FULL)) + u_fifo ( + .rst(rst), + .clk(clk), + .rd_en(fifo_rd_en), + .dout(fifo_dout), + .empty(fifo_empty), + .wr_en(wr_en), + .din(din), + .full(full), + .prog_full(prog_full), + .count(count) + ); + + // create FWFT FIFO out of non-FWFT FIFO + // public domain code from Eli Billauer + // see http://www.billauer.co.il/reg_fifo.html + assign will_update_middle = fifo_valid && (middle_valid == will_update_dout); + assign will_update_dout = (middle_valid || fifo_valid) && (rd_en || !dout_valid); + assign fifo_rd_en = (!fifo_empty) && !(middle_valid && dout_valid && fifo_valid); + assign empty = !dout_valid; + + always_ff @(posedge clk) begin + if (rst) begin + fifo_valid <= 0; + middle_valid <= 0; + dout_valid <= 0; + dout <= 0; + middle_dout <= 0; + end else begin + if (will_update_middle) + middle_dout <= fifo_dout; + + if (will_update_dout) + dout <= middle_valid ? middle_dout : fifo_dout; + + if (fifo_rd_en) + fifo_valid <= 1; + else if (will_update_middle || will_update_dout) + fifo_valid <= 0; + + if (will_update_middle) + middle_valid <= 1; + else if (will_update_dout) + middle_valid <= 0; + + if (will_update_dout) + dout_valid <= 1; + else if (rd_en) + dout_valid <= 0; + end + end + +endmodule diff --git a/modules/dem_uart/common/fifo_singleclock_standard.sv b/modules/dem_uart/common/fifo_singleclock_standard.sv new file mode 100644 index 0000000..7160c87 --- /dev/null +++ b/modules/dem_uart/common/fifo_singleclock_standard.sv @@ -0,0 +1,111 @@ +/* Copyright (c) 2017 by the author(s) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * ============================================================================= + * + * Synchronous Standard FIFO (one clock) + * + * The memory block in this FIFO is following the "RAM HDL Coding Guidelines" + * of Xilinx (UG901) to enable placing the FIFO memory into block ram during + * synthesis. + * + * Author(s): + * Philipp Wagner + */ + +module fifo_singleclock_standard #( + parameter WIDTH = 8, + parameter DEPTH = 32, + parameter PROG_FULL = DEPTH / 2 +)( + input clk, + input rst, + + input [(WIDTH-1):0] din, + input wr_en, + output full, + output prog_full, + + output reg [(WIDTH-1):0] dout, + input rd_en, + output empty, + + output [$clog2(DEPTH) - 1:0] count +); + localparam AW = $clog2(DEPTH); + + // ensure that parameters are set to allowed values + initial begin + if ((1 << $clog2(DEPTH)) != DEPTH) begin + $fatal("fifo_singleclock_standard: the DEPTH must be a power of two."); + end + end + + reg [AW-1:0] wr_addr; + reg [AW-1:0] rd_addr; + wire fifo_read; + wire fifo_write; + reg [AW-1:0] rd_count; + + // generate control signals + assign empty = (rd_count[AW-1:0] == 0); + assign prog_full = (rd_count[AW-1:0] >= PROG_FULL); + assign full = (rd_count[AW-1:0] == (DEPTH-1)); + assign fifo_read = rd_en & ~empty; + assign fifo_write = wr_en & ~full; + assign count = rd_count; + + // address logic + always_ff @(posedge clk) begin + if (rst) begin + wr_addr[AW-1:0] <= 'd0; + rd_addr[AW-1:0] <= 'b0; + rd_count[AW-1:0] <= 'b0; + end else begin + if (fifo_write & fifo_read) begin + wr_addr[AW-1:0] <= wr_addr[AW-1:0] + 'd1; + rd_addr[AW-1:0] <= rd_addr[AW-1:0] + 'd1; + end else if (fifo_write) begin + wr_addr[AW-1:0] <= wr_addr[AW-1:0] + 'd1; + rd_count[AW-1:0]<= rd_count[AW-1:0] + 'd1; + end else if (fifo_read) begin + rd_addr[AW-1:0] <= rd_addr[AW-1:0] + 'd1; + rd_count[AW-1:0]<= rd_count[AW-1:0] - 'd1; + end + end + end + + // generic dual-port, single clock memory + reg [WIDTH-1:0] ram [DEPTH-1:0]; + + // write + always_ff @(posedge clk) begin + if (fifo_write) begin + ram[wr_addr] <= din; + end + end + + // read + always_ff @(posedge clk) begin + if (fifo_read) begin + dout <= ram[rd_addr]; + end + end +endmodule diff --git a/modules/dem_uart/common/osd_dem_uart.sv b/modules/dem_uart/common/osd_dem_uart.sv index 5ebc15e..837a3b4 100644 --- a/modules/dem_uart/common/osd_dem_uart.sv +++ b/modules/dem_uart/common/osd_dem_uart.sv @@ -70,8 +70,326 @@ module osd_dem_uart .reg_err (1'b0), .reg_rdata (16'h0)); + // --------------------------------------------------------------- + logic [2:0] mod_type_sub; + logic event_available; + logic event_consumed; + reg [3:0] data_num_words; + logic data_reg_idx; + logic data_reg_valid; + logic [15:0] data; + + + // XXX: If there is packetization in the other direction as well, then + // the 'single char' packet flag must only be set for the last packet + osd_event_packetization + #(.MAX_PKT_LEN(1), .MAX_DATA_NUM_WORDS(16)) + u_event_packetization(.clk(clk), .rst(rst) + .debug_out(c_uart_out), + .debug_out_ready(c_uart_out_ready), + .id(id), + .dest(event_dest), + .overflow(1'b0), + .mod_type_sub(mod_type_sub), + .event_available(event_available), + .event_consumed(event_consumed), + .data_num_words(data_num_words), + .data_reg_idx(data_reg_idx), + .data_reg_valid(data_reg_valid), + .data(data)); + +// XXX: The entire tx/rx fifo logic could/should be a new module 'osd_dem_uart_fifo' +// that wraps 2 (or 4) fwft-fifos and allows for 8 or 16 bit reads/writes. +// Internally the module should take care of swapping MSB & LSB whenever +// only 8 bit are written/read. + +// ------------------------ TX LOGIC --------------------------------- + logic tx_fifo0_din; + logic tx_fifo0_wr_en; + logic tx_fifo0_full; + logic tx_fifo0_dout; + logic tx_fifo0_rd_en; + logic tx_fifo0_empty; + logic tx_fifo0_count; + logic tx_fifo1_din; + logic tx_fifo1_wr_en; + logic tx_fifo1_full; + logic tx_fifo1_dout; + logic tx_fifo1_rd_en; + logic tx_fifo1_empty; + logic tx_fifo1_count; + + fifo_singleclock_fwft + #(.WIDTH(16), .DEPTH(16)) + u_tx_fifo0(.clk(clk), .rst(rst), + .din(tx_fifo0_din), + .wr_en(tx_fifo0_wr_en), + .full(tx_fifo0_full), + .prog_full(), + .dout(tx_fifo0_dout), + .rd_en(tx_fifo0_rd_en), + .empty(tx_fifo0_empty), + .count(tx_fifo0_count)); + + fifo_singleclock_fwft + #(.WIDTH(16), .DEPTH(16)) + u_tx_fifo1(.clk(clk), .rst(rst), + .din(tx_fifo1_din), + .wr_en(tx_fifo1_wr_en), + .full(tx_fifo1_full), + .prog_full(), + .dout(tx_fifo1_dout), + .rd_en(tx_fifo1_rd_en), + .empty(tx_fifo1_empty), + .count(tx_fifo1_count)); + + // 0 = fifo0, 1 = fifo1 + logic tx_fifo_select, nxt_tx_fifo_select; + logic tx_fifo_select1, nxt_tx_fifo_select1; + + enum {STATE_IDLE, STATE_DELAY, STATE_XFER} + tx_state, nxt_tx_state; + + always @(posedge clk) begin + if (rst) begin + tx_fifo_select <= 1'b0; + tx_fifo_select <= 1'b0; + tx_state <= STATE_IDLE; + end else begin + tx_fifo_select <= nxt_tx_fifo_select; + tx_fifo_select1 <= nxt_tx_fifo_select1; + tx_state <= nxt_tx_state; + end + end + + reg tx_delay_couter; + reg last; + + always_comb begin + tx_fifo0_din = 8'h0; + tx_fifo0_wr_en = 1'b0; + tx_fifo1_din = 8'h0; + tx_fifo1_wr_en = 1'b0; + out_ready = 1'b0; + + if (tx_fifo_select) begin + tx_fifo0_din = out_char; + tx_fifo0_wr_en = out_valid; + out_ready = !tx_fifo0_full; + if (out_valid & out_ready) begin + nxt_tx_fifo_select = 1'b0; + end + end else begin + tx_fifo1_din = out_char; + tx_fifo1_wr_en = out_valid; + out_ready = !tx_fifo1_full; + if (out_valid & out_ready) begin + nxt_tx_fifo_select = 1'b1; + end + end + + case (tx_state) + STATE_IDLE: + if (!tx_fifo01_empty) begin + nxt_tx_state = STATE_DELAY; + end + end + STATE_DELAY: + // XXX: Magic numbers + if (tx_delay_couter > 20 | tx_fifo01_count > 8) begin + event_available = 1'b1; + nxt_tx_state = STATE_XFER; + data_num_words = tx_fifo0_count + tx_fifo1_count; + mod_type_sub = {(tx_fifo0_count == tx_fifo1_count), 2'b00} + end else begin + tx_delay_couter = tx_delay_couter + 1; + end + end + STATE_XFER: + if (data_reg_valid) begin + // TODO: Current data_word_idx + nxt + // TODO: We need a tx_fifo_select1 + end + + if (event_consumed) begin + nxt_tx_state = STATE_IDLE; + end + end + endcase + end +// ------------------------- TX LOGIC END ------------------------- + + +// ------------------------- RX LOGIC ----------------------------- + logic rx_fifo0_din; + logic rx_fifo0_wr_en; + logic rx_fifo0_full; + logic rx_fifo0_dout; + logic rx_fifo0_rd_en; + logic rx_fifo0_empty; + logic rx_fifo0_count; + logic rx_fifo1_din; + logic rx_fifo1_wr_en; + logic rx_fifo1_full; + logic rx_fifo1_dout; + logic rx_fifo1_rd_en; + logic rx_fifo1_empty; + logic rx_fifo1_count; + + fifo_singleclock_fwft + #(.WIDTH(16), .DEPTH(16)) + u_rx_fifo0(.clk(clk), .rst(rst), + .din(rx_fifo0_din), + .wr_en(rx_fifo0_wr_en), + .full(rx_fifo0_full), + .prog_full(), + .dout(rx_fifo0_dout), + .rd_en(rx_fifo0_rd_en), + .empty(rx_fifo0_empty), + .count(rx_fifo0_count)); + + fifo_singleclock_fwft + #(.WIDTH(16), .DEPTH(16)) + u_rx_fifo1(.clk(clk), .rst(rst), + .din(rx_fifo1_din), + .wr_en(rx_fifo1_wr_en), + .full(rx_fifo1_full), + .prog_full(), + .dout(rx_fifo1_dout), + .rd_en(rx_fifo1_rd_en), + .empty(rx_fifo1_empty), + .count(rx_fifo1_count)); + + logic rx_fifo_select, nxt_rx_fifo_select; + logic rx_fifo_select1, nxt_rx_fifo_select1; + + enum { STATE_IDLE, STATE_HDR_SRC, STATE_HDR_FLAGS, + STATE_XFER } rx_state, nxt_rx_state; + + always @(posedge clk) begin + if (rst) begin + rx_fifo_select <= 1'b0; + rx_fifo_select1 <= 1'b0; + rx_state <= STATE_IDLE; + end else begin + rx_fifo_select <= nxt_rx_fifo_select; + rx_fifo_select1 <= nxt_rx_fifo_select1; + rx_state <= nxt_rx_state; + end + end + + reg is_single_char; + + always_comb begin + in_char = 8'h0; + in_valid = 1'b0; + + if (rx_fifo_select) begin + in_char = rx_fifo0_dout; + in_valid = !rx_fifo0_empty; + rx_fifo0_rd_en = in_ready; + if (in_valid & in_ready) begin + nxt_rx_fifo_select = 1'b0; + end + end else begin + in_char = rx_fifo1_dout; + in_valid = !rx_fifo1_empty; + rx_fifo1_rd_en = in_ready; + if (in_valid & in_ready) begin + nxt_rx_fifo_select = 1'b01; + end + end + + c_uart_in_ready = 0; + nxt_rx_fifo_select = 1'b0; + rx_fifo0_wr_en = 1'b0; + rx_fifo0_din = 8'h0; + rx_fifo1_wr_en = 1'b0; + rx_fifo1_din = 8'h0; + + case (rx_state) + STATE_IDLE: begin + c_uart_in_ready = 1; + if (c_uart_in.valid) begin + nxt_rx_state = STATE_HDR_SRC; + end + end + STATE_HDR_SRC: begin + c_uart_in_ready = 1; + if (c_uart_in.valid) begin + nxt_rx_state = STATE_HDR_FLAGS; + end + end + STATE_HDR_FLAGS: begin + c_uart_in_ready = 1; + is_single_char = c_uart_in.data[12]; // XXX: Choose correct bit + if (c_uart_in.valid) begin + nxt_rx_state = STATE_XFER; + end + end + STATE_XFER: begin + // XXX: Simplify this block + if (c_uart_in.last) begin + if (is_single_char) begin + if (rx_fifo_select1) begin + c_uart_in_ready = !rx_fifo1_full; + + rx_fifo1_wr_en = c_uart_in.valid; + rx_fifo1_din = c_uart_in.data[7:0]; + nxt_rx_fifo_select1 = 1'b0; + end else begin + c_uart_in_ready = !rx_fifo0_full; + + rx_fifo0_wr_en = c_uart_in.valid; + rx_fifo0_din = c_uart_in.data[7:0]; + nxt_rx_fifo_select1 = 1'b1; + end + end else begin + if (rx_fifo_select1) begin + c_uart_in_ready = !rx_fifo0_full & !rx_fifo1_full; + + rx_fifo0_wr_en = c_uart_in.valid; + rx_fifo0_din = c_uart_in.data[15:8]; + rx_fifo1_wr_en = c_uart_in.valid; + rx_fifo1_din = c_uart_in.data[7:0]; + end else begin + c_uart_in_ready = !rx_fifo0_full & !rx_fifo1_full; + + rx_fifo0_wr_en = c_uart_in.valid; + rx_fifo0_din = c_uart_in.data[7:0]; + rx_fifo1_wr_en = c_uart_in.valid; + rx_fifo1_din = c_uart_in.data[15:8]; + end + end + end else begin + if (rx_fifo_select1) begin + c_uart_in_ready = !rx_fifo0_full & !rx_fifo1_full; + + rx_fifo0_wr_en = c_uart_in.valid; + rx_fifo0_din = c_uart_in.data[15:8]; + rx_fifo1_wr_en = c_uart_in.valid; + rx_fifo1_din = c_uart_in.data[7:0]; + end else begin + c_uart_in_ready = !rx_fifo0_full & !rx_fifo1_full; + + rx_fifo0_wr_en = c_uart_in.valid; + rx_fifo0_din = c_uart_in.data[7:0]; + rx_fifo1_wr_en = c_uart_in.valid; + rx_fifo1_din = c_uart_in.data[15:8]; + end + end + + if (c_uart_in.valid && c_uart_in.last) begin + nxt_rx_state = STATE_IDLE; + end + end + endcase + end +// ------------------------ RX LOGIC END --------------------------- + +// ---------------------- OLD CODE --------------------------------- enum { STATE_IDLE, STATE_HDR_DEST, STATE_HDR_SRC, STATE_HDR_FLAGS, - STATE_XFER } state_tx, state_rx; + STATE_XFER } state_rx; always @(posedge clk) begin if (rst) begin