diff --git a/Semaine_6/UART_ULTRASON_COMMANDS/.gitignore b/Semaine_6/UART_ULTRASON_COMMANDS/.gitignore new file mode 100644 index 0000000..029ea4f --- /dev/null +++ b/Semaine_6/UART_ULTRASON_COMMANDS/.gitignore @@ -0,0 +1,5 @@ +runs +.vscode +workspace.code-workspace +*.pyc +.idea \ No newline at end of file diff --git a/Semaine_6/UART_ULTRASON_COMMANDS/IP/verilog/fifo.v b/Semaine_6/UART_ULTRASON_COMMANDS/IP/verilog/fifo.v new file mode 100644 index 0000000..82915e2 --- /dev/null +++ b/Semaine_6/UART_ULTRASON_COMMANDS/IP/verilog/fifo.v @@ -0,0 +1,48 @@ + module fifo #( + parameter SIZE = 16, + parameter WIDTH = 8 +)( + input wire clk, + input wire wr_en, + input wire[WIDTH-1:0] wr_data, + input wire rd_en, + output reg[WIDTH-1:0] rd_data, + + output wire full, + output wire empty +); + + localparam LOGSIZE = $clog2(SIZE); + + reg [WIDTH-1:0] fifo[0:SIZE-1]; + reg [LOGSIZE-1:0] wr_ptr; + reg [LOGSIZE-1:0] rd_ptr; + reg [LOGSIZE:0] count; + + assign full = (count == SIZE); + assign empty = (count == 0); + + initial begin + wr_ptr = 0; + rd_ptr = 0; + count = 0; + end + + always @(posedge clk) begin // IN + rd_data <= fifo[rd_ptr]; + if (wr_en && !full && rd_en && !empty) begin + fifo[wr_ptr] <= wr_data; + wr_ptr <= (wr_ptr == SIZE - 1) ? 0 : (wr_ptr + 1) ; + rd_ptr <= (rd_ptr == SIZE - 1) ? 0 : (rd_ptr + 1) ; + end else if (wr_en && !full) begin + fifo[wr_ptr] <= wr_data; + wr_ptr <= (wr_ptr == SIZE - 1) ? 0 : (wr_ptr + 1) ; + count <= count + 1; + end else if (rd_en && !empty) begin // OUT + rd_ptr <= (rd_ptr == SIZE - 1) ? 0 : (rd_ptr + 1) ; + count <= count - 1; + end + + end + +endmodule diff --git a/Semaine_6/UART_ULTRASON_COMMANDS/IP/verilog/rxuartlite.v b/Semaine_6/UART_ULTRASON_COMMANDS/IP/verilog/rxuartlite.v new file mode 100644 index 0000000..a527848 --- /dev/null +++ b/Semaine_6/UART_ULTRASON_COMMANDS/IP/verilog/rxuartlite.v @@ -0,0 +1,796 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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-2024, 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 +// 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 = 234, // 115200 Baud at 100MHz +`endif + 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 + // }}} + ) ( + // {{{ + input wire i_clk, i_reset, + input wire i_uart_rx, + output reg o_wr, + output reg [7:0] o_data + // }}} + ); + + // 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) + if (i_reset) + { ck_uart, qq_uart, q_uart } <= 3'b111; + else + { 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 (i_reset) + chg_counter <= {(TB){1'b1}}; + else 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) + if (i_reset) + half_baud_time <= 0; + else + half_baud_time <= (!ck_uart)&&(chg_counter >= half_baud-1'b1-1'b1); + // }}} + + // state + // {{{ + initial state = RXUL_IDLE; + always @(posedge i_clk) + if (i_reset) + begin + state <= RXUL_IDLE; + end else 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 (i_reset) + begin + o_wr <= 1'b0; + o_data <= 8'h00; + end else 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 (i_reset) + baud_counter <= 0; + else 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 (i_reset) + zero_baud_counter <= 1'b1; + else 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 + localparam F_CKRES = 10; + + (* anyseq *) wire f_tx_start; + (* anyconst *) wire [(F_CKRES-1):0] f_tx_step; + (* gclk *) wire gbl_clk; + 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 @(posedge gbl_clk) + f_rx_clock <= f_rx_clock + 1'b1; + + always @(*) + assume(i_clk == f_rx_clock[1]); + + always @(posedge gbl_clk) + if (!$rose(i_clk)) + assume(!$fell(i_reset)); + + + //////////////////////////////////////////////////////////////////////// + // + // 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 @(posedge gbl_clk) + 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); + + always @(*) + if (i_reset) + assume(i_uart_rx); + + //////////////////////////////////////////////////////////////////////// + // + // The simulated timing generator + + always @(*) + if (i_reset) + assume(!f_tx_busy); + + always @(*) + if (f_tx_busy || i_reset) + assume(!f_tx_start); + + always @(*) + if (i_reset) + assume(f_tx_baud == CLOCKS_PER_BAUD-1); + + 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; + else if (!f_tx_zbaud) + f_tx_baud <= f_tx_baud - 1; + + 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 @(posedge gbl_clk) + if ((f_past_valid)&&(!$rose(f_txclk))) + assume($stable(f_tx_data)); + else if (f_tx_busy) + assume($stable(f_tx_data)); + + // + always @(posedge gbl_clk) + 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]) + begin + assert(f_tx_reg[8:0] == { f_tx_data, 1'b0 }); + end else if (f_tx_reg[8]) + begin + assert(f_tx_reg[7:0] == f_tx_data[7:0] ); + end else if (f_tx_reg[7]) + begin + assert(f_tx_reg[6:0] == f_tx_data[7:1] ); + end else if (f_tx_reg[6]) + begin + assert(f_tx_reg[5:0] == f_tx_data[7:2] ); + end else if (f_tx_reg[5]) + begin + assert(f_tx_reg[4:0] == f_tx_data[7:3] ); + end else if (f_tx_reg[4]) + begin + assert(f_tx_reg[3:0] == f_tx_data[7:4] ); + end else if (f_tx_reg[3]) + begin + assert(f_tx_reg[2:0] == f_tx_data[7:5] ); + end else if (f_tx_reg[2]) + begin + assert(f_tx_reg[1:0] == f_tx_data[7:6] ); + end else if (f_tx_reg[1]) + begin + assert(f_tx_reg[0] == f_tx_data[7]); + end + + // 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]) + begin + assert(f_tx_count == + CLOCKS_PER_BAUD -1 -f_tx_baud); + end else if (f_tx_reg[8]) + begin + assert(f_tx_count == + 2 * CLOCKS_PER_BAUD -1 -f_tx_baud); + end else if (f_tx_reg[7]) + begin + assert(f_tx_count == + 3 * CLOCKS_PER_BAUD -1 -f_tx_baud); + end else if (f_tx_reg[6]) + begin + assert(f_tx_count == + 4 * CLOCKS_PER_BAUD -1 -f_tx_baud); + end else if (f_tx_reg[5]) + begin + assert(f_tx_count == + 5 * CLOCKS_PER_BAUD -1 -f_tx_baud); + end else if (f_tx_reg[4]) + begin + assert(f_tx_count == + 6 * CLOCKS_PER_BAUD -1 -f_tx_baud); + end else if (f_tx_reg[3]) + begin + assert(f_tx_count == + 7 * CLOCKS_PER_BAUD -1 -f_tx_baud); + end else if (f_tx_reg[2]) + begin + assert(f_tx_count == + 8 * CLOCKS_PER_BAUD -1 -f_tx_baud); + end else if (f_tx_reg[1]) + begin + assert(f_tx_count == + 9 * CLOCKS_PER_BAUD -1 -f_tx_baud); + end else if (f_tx_reg[0]) + begin + assert(f_tx_count == + 10 * CLOCKS_PER_BAUD -1 -f_tx_baud); + end else begin + assert(f_tx_count == + 11 * CLOCKS_PER_BAUD -1 -f_tx_baud); + end + + // }}} + //////////////////////////////////////////////////////////////////////// + // + // 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 (i_reset) + f_rx_count <= 0; + else 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) + case(state) + 0: assert(f_rx_count == half_baud + (CLOCKS_PER_BAUD-baud_counter)); + 1: assert(f_rx_count == half_baud + 2 * CLOCKS_PER_BAUD + - baud_counter); + 2: assert(f_rx_count == half_baud + 3 * CLOCKS_PER_BAUD + - baud_counter); + 3: assert(f_rx_count == half_baud + 4 * CLOCKS_PER_BAUD + - baud_counter); + 4: assert(f_rx_count == half_baud + 5 * CLOCKS_PER_BAUD + - baud_counter); + 5: assert(f_rx_count == half_baud + 6 * CLOCKS_PER_BAUD + - baud_counter); + 6: assert(f_rx_count == half_baud + 7 * CLOCKS_PER_BAUD + - baud_counter); + 7: assert(f_rx_count == half_baud + 8 * CLOCKS_PER_BAUD + - baud_counter); + 8: assert((f_rx_count == half_baud + 9 * CLOCKS_PER_BAUD + - baud_counter) + ||(f_rx_count == half_baud + 10 * CLOCKS_PER_BAUD + - baud_counter)); + 9: begin end + 4'hf: begin end + default: + assert(1'b0); + endcase + + 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(i_reset) + && $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 @(posedge gbl_clk) + if (!f_past_valid || i_reset) + { ck_tx_clock, q_tx_clock } <= 0; + else + { ck_tx_clock, q_tx_clock } <= { q_tx_clock, f_tx_clock }; + always @(posedge gbl_clk) + if (!f_past_valid || i_reset) + { ck_tx_count, q_tx_count } <= 0; + else + { 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 @(posedge gbl_clk) + if (state == RXUL_WAIT) + assert((!f_tx_busy)||(f_tx_reg[9:1] == 0)); + + always @(posedge gbl_clk) + if (f_past_valid && !$past(i_reset)) + begin + if (state == RXUL_IDLE) + begin + assert((!f_tx_busy)||(f_tx_reg[9])||(f_tx_reg[9:1]==0)); + if (ck_uart) + assert((f_tx_reg[9:1]==0)||(f_tx_count < (3 + CLOCKS_PER_BAUD/2))); + end else if (state == 0) + begin + assert(f_sub_baud_difference + <= 2 * ((CLOCKS_PER_BAUD< 6)) + // assert(i_uart_rx == ck_uart); + + // Make sure the data register matches + always @(posedge i_clk) + case(state) + 4'h0: assert(!data_reg[7]); + 4'h1: assert((data_reg[7] == $past(f_tx_data[0]))&&(!data_reg[6])); + 4'h2: assert(data_reg[7:6] == $past(f_tx_data[1:0])); + 4'h3: assert(data_reg[7:5] == $past(f_tx_data[2:0])); + 4'h4: assert(data_reg[7:4] == $past(f_tx_data[3:0])); + 4'h5: assert(data_reg[7:3] == $past(f_tx_data[4:0])); + 4'h6: assert(data_reg[7:2] == $past(f_tx_data[5:0])); + 4'h7: assert(data_reg[7:1] == $past(f_tx_data[6:0])); + 4'h8: assert(data_reg[7:0] == $past(f_tx_data[7:0])); + endcase + // }}} + //////////////////////////////////////////////////////////////////////// + // + // Cover properties + // {{{ + //////////////////////////////////////////////////////////////////////// + // + always @(posedge i_clk) + cover(o_wr); // Step 626, takes about 20mins + + always @(posedge i_clk) + if (!i_reset && f_past_valid && !$past(i_reset)) + 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/Semaine_6/UART_ULTRASON_COMMANDS/IP/verilog/txuartlite.v b/Semaine_6/UART_ULTRASON_COMMANDS/IP/verilog/txuartlite.v new file mode 100644 index 0000000..c43edfe --- /dev/null +++ b/Semaine_6/UART_ULTRASON_COMMANDS/IP/verilog/txuartlite.v @@ -0,0 +1,472 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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-2024, 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 +// 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'd8, + localparam TB = TIMING_BITS, + // CLOCKS_PER_BAUD -- the number of system clocks per baud + // interval. + parameter [(TB-1):0] CLOCKS_PER_BAUD = 234 // 24'd868 + // }}} + ) ( + // {{{ + input wire i_clk, i_reset, + 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 + // }}} + ); + + // 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) + if (i_reset) + begin + r_busy <= 1'b1; + state <= TXUL_IDLE; + end else 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 + // }}} + + // 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_reset) + lcl_data <= 8'hff; + else 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_reset) + o_uart_tx <= 1'b1; + else 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) + if (i_reset) + begin + zero_baud_counter <= 1'b1; + baud_counter <= 0; + end else 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(i_reset) && $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(i_reset) && !$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(i_reset)&& !$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/Semaine_6/UART_ULTRASON_COMMANDS/IP/verilog/uart_rx_fifo.v b/Semaine_6/UART_ULTRASON_COMMANDS/IP/verilog/uart_rx_fifo.v new file mode 100644 index 0000000..d521fde --- /dev/null +++ b/Semaine_6/UART_ULTRASON_COMMANDS/IP/verilog/uart_rx_fifo.v @@ -0,0 +1,56 @@ +module uart_rx_fifo #( + parameter CLK_FREQ = 27_000_000, + parameter BAUD_RATE = 115200, + parameter FIFO_SIZE = 8 +)( + input clk, + input rd_en, + output wire [7:0] rd_data, + input rx_pin, + output data_available +); + + // UART RX wires + wire [7:0] rx_data; + wire rx_received; + + // FIFO control + reg wr_en; + wire fifo_empty; + wire fifo_full; + + // UART Receiver instance + rxuartlite uart_rx_inst ( + .i_clk(clk), + .i_reset(1'b0), + .i_uart_rx(rx_pin), + .o_wr(rx_received), + .o_data(rx_data) + ); + + // FIFO instance + fifo #( + .WIDTH(8), + .SIZE(FIFO_SIZE) + ) fifo_inst ( + .clk(clk), + .wr_en(wr_en), + .wr_data(rx_data), + .rd_en(rd_en), + .rd_data(rd_data), + .empty(fifo_empty), + .full(fifo_full) + ); + + assign data_available = ~fifo_empty; + + // Écriture dans la FIFO uniquement si donnée reçue ET FIFO pas pleine + always @(posedge clk) begin + if (rx_received && !fifo_full) begin + wr_en <= 1'b1; + end else begin + wr_en <= 1'b0; + end + end + +endmodule \ No newline at end of file diff --git a/Semaine_6/UART_ULTRASON_COMMANDS/IP/verilog/uart_tx.v b/Semaine_6/UART_ULTRASON_COMMANDS/IP/verilog/uart_tx.v new file mode 100644 index 0000000..9f48d93 --- /dev/null +++ b/Semaine_6/UART_ULTRASON_COMMANDS/IP/verilog/uart_tx.v @@ -0,0 +1,131 @@ +module uart_tx #( + parameter CLK_FREQ = 27_000_000, + parameter BAUD_RATE = 115200 +)( + input wire clk, + input wire rst_p, + input wire[7:0] data, + input wire tx_enable, + + output reg tx_ready, + output wire tx +); + + localparam CYCLE = CLK_FREQ / BAUD_RATE; + + localparam IDLE = 2'd0; + localparam START = 2'd1; + localparam DATA = 2'd2; + localparam STOP = 2'd3; + + reg [1:0] state = IDLE; + reg [1:0] next_state; + reg [15:0] cycle_cnt; //baud counter + reg tx_reg; + reg [2:0] bit_cnt; + reg [7:0] tx_data_latch = 0; + + + assign tx = tx_reg; + + always@(posedge clk or posedge rst_p)begin // Avance d'etat + if(rst_p == 1'b1) + state <= IDLE; + else + state <= next_state; + end + + always@(*) begin + case(state) + IDLE: + if(tx_enable == 1'b1) + next_state = START; + else + next_state = IDLE; + + START: + if(cycle_cnt == CYCLE - 1) + next_state = DATA; + else + next_state = START; + + DATA: + if(cycle_cnt == CYCLE - 1 && bit_cnt == 3'd7) + next_state = STOP; + else + next_state = DATA; + + STOP: + if(cycle_cnt == CYCLE - 1) + next_state = IDLE; + else + next_state = STOP; + default: + next_state = IDLE; + endcase + end + + always@(posedge clk or posedge rst_p)begin // tx_ready block + if(rst_p == 1'b1) + tx_ready <= 1'b0; // Reset + else if(state == IDLE && tx_enable == 1'b1) + tx_ready <= 1'b0; // Pas prêt tant que les données sont valides + else if(state == IDLE) + tx_ready <= 1'b1; + else if(state == STOP && cycle_cnt == CYCLE - 1) + tx_ready <= 1'b1; // Prêt une fois le bit STOP envoyé + else + tx_ready <= tx_ready; // Reste inchangé dans d'autres cas + end + + + + always@(posedge clk or posedge rst_p) begin // tx_data_latch block + if(rst_p == 1'b1) begin + tx_data_latch <= 8'd0; + end else if(state == IDLE && tx_enable == 1'b1) begin + tx_data_latch <= data; // Charger les données de data dans tx_data_latch + end + end + + + always@(posedge clk or posedge rst_p)begin // DATA bit_cnt block + if(rst_p == 1'b1)begin + bit_cnt <= 3'd0; + + end else if(state == DATA) + if(cycle_cnt == CYCLE - 1) + bit_cnt <= bit_cnt + 3'd1; + else + bit_cnt <= bit_cnt; + else + bit_cnt <= 3'd0; + end + + + always@(posedge clk or posedge rst_p)begin // Cycle counter + if(rst_p == 1'b1) + cycle_cnt <= 16'd0; + + else if((state == DATA && cycle_cnt == CYCLE - 1) || next_state != state) + cycle_cnt <= 16'd0; + else + cycle_cnt <= cycle_cnt + 16'd1; + end + + always@(posedge clk or posedge rst_p)begin // tx state managment + if(rst_p == 1'b1) + tx_reg <= 1'b1; + else + case(state) + IDLE,STOP: + tx_reg <= 1'b1; + START: + tx_reg <= 1'b0; + DATA: + tx_reg <= tx_data_latch[bit_cnt]; // SENDING BYTE HERE + default: + tx_reg <= 1'b1; + endcase + end +endmodule diff --git a/Semaine_6/UART_ULTRASON_COMMANDS/IP/verilog/uart_tx_fifo.v b/Semaine_6/UART_ULTRASON_COMMANDS/IP/verilog/uart_tx_fifo.v new file mode 100644 index 0000000..5d5387b --- /dev/null +++ b/Semaine_6/UART_ULTRASON_COMMANDS/IP/verilog/uart_tx_fifo.v @@ -0,0 +1,89 @@ +module uart_tx_fifo #( + parameter CLK_FREQ = 27_000_000, + parameter BAUD_RATE = 115200, + parameter FIFO_SIZE = 8 +)( + input clk, + input wr_en, + input [7:0] wr_data, + output tx_pin, + output fifo_full +); + + // FIFO wires + wire [7:0] fifo_rd_data; + wire fifo_empty; + reg fifo_rd_en; + + // UART wires + wire tx_busy; + reg uart_tx_enable; + reg [7:0] uart_tx_data; + + // FSM + typedef enum logic [1:0] { + IDLE, + WAIT_READY, + READ_FIFO, + SEND + } state_t; + + state_t state = IDLE; + + // FIFO instantiation + fifo #( + .WIDTH(8), + .SIZE(FIFO_SIZE) + ) fifo_inst ( + .clk(clk), + .wr_en(wr_en), + .wr_data(wr_data), + .rd_en(fifo_rd_en), + .rd_data(fifo_rd_data), + .empty(fifo_empty), + .full(fifo_full) + ); + + // UART TX instantiation + txuartlite uart_tx_inst ( + .i_clk(clk), + .i_reset(1'b0), + .i_wr(uart_tx_enable), + .i_data(uart_tx_data), + .o_uart_tx(tx_pin), + .o_busy(tx_busy) + ); + + always_ff @(posedge clk) begin + fifo_rd_en <= 0; + uart_tx_enable <= 0; + + case (state) + IDLE: begin + if (!fifo_empty) + state <= WAIT_READY; + end + + WAIT_READY: begin + if (!tx_busy) begin + fifo_rd_en <= 1; + uart_tx_data <= fifo_rd_data; + state <= READ_FIFO; + end + end + + READ_FIFO: begin + // fifo_rd_data sera valide ici + fifo_rd_en <= 0; + uart_tx_enable <= 1; + state <= SEND; + end + + SEND: begin + state <= IDLE; + uart_tx_enable <= 0; + end + endcase + end + +endmodule \ No newline at end of file diff --git a/Semaine_6/UART_ULTRASON_COMMANDS/IP/verilog/ultrasonic_fpga.v b/Semaine_6/UART_ULTRASON_COMMANDS/IP/verilog/ultrasonic_fpga.v new file mode 100644 index 0000000..c11d19e --- /dev/null +++ b/Semaine_6/UART_ULTRASON_COMMANDS/IP/verilog/ultrasonic_fpga.v @@ -0,0 +1,152 @@ +module ultrasonic_fpga #( + parameter integer CLK_FREQ = 27_000_000 // Fréquence d'horloge en Hz +)( + input wire clk, + input wire start, + inout wire sig, // Broche bidirectionnelle vers le capteur + output reg [15:0] distance, // Distance mesurée en cm + output reg busy, + output reg done +); + reg [15:0] trig_counter = 0; + reg [31:0] echo_counter = 0; + reg [31:0] echo_div_counter = 0; + reg [15:0] distance_counter = 0; + + reg sig_out; + reg sig_dir; // 1: output, 0: input + + assign sig = sig_dir ? sig_out : 1'bz; // bz pour dire que le fpga laisse le fils libre et n'oblige pas de valeur + + reg sig_int, sig_ok; + + + localparam IDLE = 3'd0, + TRIG_HIGH = 3'd1, + TRIG_LOW = 3'd2, + WAIT_ECHO = 3'd3, + MEASURE_ECHO = 3'd4, + COMPUTE = 3'd5, + DONE = 3'd6, + WAIT_NEXT = 3'd7; + reg [2:0] state = IDLE; + + + localparam integer TRIG_PULSE_CYCLES = CLK_FREQ / 100_000; // 10us pulse + localparam integer DIST_DIVISOR = (58 * CLK_FREQ) / 1_000_000; // pour conversion us -> cm + localparam integer MAX_CM = 350; + localparam integer TIMEOUT_CYCLES = (MAX_CM * 58 * CLK_FREQ) / 1000000; + + localparam WAIT_NEXT_CYCLES = (CLK_FREQ / 1000) * 100; // 60 ms + + reg [31:0] wait_counter; + + always @(posedge clk) begin + sig_int <= sig; + sig_ok <= sig_int; + end + + always @(posedge clk) begin + busy <= (state != IDLE); + end + + always @(posedge clk) begin // FSM + + case (state) + IDLE: begin + done <= 0; + sig_out <= 0; + sig_dir <= 0; + distance <= 0; + if (start) begin + state <= TRIG_HIGH; + trig_counter <= 0; + done <= 0; + end + end + + TRIG_HIGH: begin + sig_out <= 1; + sig_dir <= 1; + if (trig_counter < TRIG_PULSE_CYCLES) begin + trig_counter <= trig_counter + 1; + end else begin + trig_counter <= 0; + state <= TRIG_LOW; + end + end + + TRIG_LOW: begin + sig_out <= 0; + sig_dir <= 0; // Mettre en entrée + + if (sig_ok) begin + state <= TRIG_LOW; + end else + state <= WAIT_ECHO; + end + + WAIT_ECHO: begin + if (sig_ok) begin + echo_counter <= 0; + state <= MEASURE_ECHO; + end else if (echo_counter >= TIMEOUT_CYCLES) begin + distance <= 0; + state <= DONE; + end else begin + echo_counter <= echo_counter + 1; + end + end + + MEASURE_ECHO: begin + if (sig_ok) begin + if (echo_counter < TIMEOUT_CYCLES) begin + echo_counter <= echo_counter + 1; + end else begin + state <= DONE; + end + + end else begin + state <= COMPUTE; + end + end + + COMPUTE: begin + if (echo_counter >= DIST_DIVISOR) begin + echo_counter <= echo_counter - DIST_DIVISOR; + distance_counter <= distance_counter + 1; + state <= COMPUTE; + end else begin + distance <= distance_counter; + state <= DONE; + end + end + + DONE: begin + if (start) begin + wait_counter <= 0; + state <= WAIT_NEXT; + end else begin + state <= IDLE; + end + done <= 1; + end + + WAIT_NEXT: begin + wait_counter <= wait_counter + 1; + if (wait_counter >= WAIT_NEXT_CYCLES) begin + state <= TRIG_HIGH; + trig_counter <= 0; + distance_counter <= 0; + echo_counter <= 0; + end + end + + default: begin + state <= IDLE; // Reset to IDLE state in case of an error + end + endcase + + end + +endmodule \ No newline at end of file diff --git a/Semaine_6/UART_ULTRASON_COMMANDS/IP/verilog/ultrasonic_sensor.v b/Semaine_6/UART_ULTRASON_COMMANDS/IP/verilog/ultrasonic_sensor.v new file mode 100644 index 0000000..ea560c0 --- /dev/null +++ b/Semaine_6/UART_ULTRASON_COMMANDS/IP/verilog/ultrasonic_sensor.v @@ -0,0 +1,95 @@ +module ultrasonic_sensor( // Simulation of an ultrasonic sensor + input wire clk, + inout wire signal // Signal from the ultrasonic sensor +); + parameter integer CLK_FREQ = 27_000_000; + + reg [2:0] state = 3'd0; // State of the FSM + reg [2:0] next_state; + reg sig_dir; // 1: output, 0: input + reg [15:0] trig_counter = 0; // Counter for the trigger pulse + reg [31:0] echo_counter = 0; // Echo signal + reg valid_trig = 0; // Valid trigger signal + + reg echo_sended = 0; // Flag to indicate if echo has been sent + + reg signal_out = 0; + assign signal = sig_dir ? signal_out : 1'bz; // Assign the signal to the output if sig_dir is high, otherwise set it to high impedance + + localparam S_WAIT_TRIG = 3'd0, + S_MEASURE_TRIG = 3'd1, + S_SEND_ECHO = 3'd2; + + localparam integer TRIG_PULSE_CYCLES = CLK_FREQ / 100_000; // 10us pulse + + always @(*) begin + case (state) + S_WAIT_TRIG: begin + sig_dir = 0; + if (signal == 1) begin + next_state = S_MEASURE_TRIG; + end else begin + next_state = S_WAIT_TRIG; + end + end + + S_MEASURE_TRIG: begin + sig_dir = 0; + if (valid_trig)begin + next_state = S_SEND_ECHO; + end + end + + S_SEND_ECHO: begin + sig_dir = 1; // Mettre en sortie + + if (echo_sended) begin + echo_sended = 0; // Reset flag + next_state = S_WAIT_TRIG; + end else begin + next_state = S_SEND_ECHO; + end + end + + default: begin + sig_dir = 0; + next_state = S_WAIT_TRIG; + end + endcase + end + + always @(posedge clk) begin + state <= next_state; + end + + always @(posedge clk) begin + if (state == S_MEASURE_TRIG) begin + if (signal == 1) begin + trig_counter <= trig_counter + 1; + end else begin + if (trig_counter >= TRIG_PULSE_CYCLES-20) begin + valid_trig <= 1; + end else begin + valid_trig <= 0; + end + end + end + end + + reg [15:0] echo_delay_counter; + + always @(posedge clk) begin + if (state == S_SEND_ECHO) begin + if (echo_delay_counter == 5800) begin // + signal_out <= 0; + echo_sended <= 1; + end else begin + signal_out <= 1; + echo_delay_counter <= echo_delay_counter + 1; + end + end else begin + echo_delay_counter <= 0; + end + end + +endmodule \ No newline at end of file diff --git a/Semaine_6/UART_ULTRASON_COMMANDS/README.md b/Semaine_6/UART_ULTRASON_COMMANDS/README.md new file mode 100644 index 0000000..34702e3 --- /dev/null +++ b/Semaine_6/UART_ULTRASON_COMMANDS/README.md @@ -0,0 +1,9 @@ +# ULTRASON VIA UART + +## Description +This project is designed to control an ultrasonic sensor using UART communication. The ultrasonic sensor is used to measure distance, and the data is transmitted via UART to a connected device. + +## Commands +0x01: Start one mesurement of the distance. +0x02: Start continuous mesurement of the distance. +0x03: Stop continuous mesurement of the distance. \ No newline at end of file diff --git a/Semaine_6/UART_ULTRASON_COMMANDS/constraints/top_uart_ultrason_command.cst b/Semaine_6/UART_ULTRASON_COMMANDS/constraints/top_uart_ultrason_command.cst new file mode 100644 index 0000000..110a02b --- /dev/null +++ b/Semaine_6/UART_ULTRASON_COMMANDS/constraints/top_uart_ultrason_command.cst @@ -0,0 +1,24 @@ +IO_LOC "tx" 69; +IO_PORT "tx" IO_TYPE=LVCMOS33 PULL_MODE=UP BANK_VCCIO=3.3; + +IO_LOC "rx" 70; +IO_PORT "rx" IO_TYPE=LVCMOS33 PULL_MODE=UP BANK_VCCIO=3.3; + +IO_LOC "clk" 4; +IO_PORT "clk" IO_TYPE=LVCMOS33 PULL_MODE=UP BANK_VCCIO=3.3; + +IO_LOC "ultrason_sig" 73; +IO_PORT "ultrason_sig" IO_TYPE=LVCMOS33 PULL_MODE=UP DRIVE=8 BANK_VCCIO=3.3; + +IO_LOC "leds[0]" 15; +IO_PORT "leds[0]" PULL_MODE=UP DRIVE=8 BANK_VCCIO=1.8; +IO_LOC "leds[1]" 16; +IO_PORT "leds[1]" PULL_MODE=UP DRIVE=8 BANK_VCCIO=1.8; +IO_LOC "leds[2]" 17; +IO_PORT "leds[2]" PULL_MODE=UP DRIVE=8 BANK_VCCIO=1.8; +IO_LOC "leds[3]" 18; +IO_PORT "leds[3]" PULL_MODE=UP DRIVE=8 BANK_VCCIO=1.8; +IO_LOC "leds[4]" 19; +IO_PORT "leds[4]" PULL_MODE=UP DRIVE=8 BANK_VCCIO=1.8; +IO_LOC "leds[5]" 20; +IO_PORT "leds[5]" PULL_MODE=UP DRIVE=8 BANK_VCCIO=1.8; \ No newline at end of file diff --git a/Semaine_6/UART_ULTRASON_COMMANDS/project.bat b/Semaine_6/UART_ULTRASON_COMMANDS/project.bat new file mode 100644 index 0000000..c087898 --- /dev/null +++ b/Semaine_6/UART_ULTRASON_COMMANDS/project.bat @@ -0,0 +1,6 @@ +@call c:\oss-cad-suite\environment.bat +@echo off +if "%1"=="sim" call scripts\windows\simulate.bat +if "%1"=="wave" call scripts\windows\gtkwave.bat +if "%1"=="clean" call scripts\windows\clean.bat +if "%1"=="build" call scripts\windows\build.bat \ No newline at end of file diff --git a/Semaine_6/UART_ULTRASON_COMMANDS/project.sh b/Semaine_6/UART_ULTRASON_COMMANDS/project.sh new file mode 100644 index 0000000..7c0497b --- /dev/null +++ b/Semaine_6/UART_ULTRASON_COMMANDS/project.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +# Charger l'environnement OSS CAD Suite +source /home/louis/oss-cad-suite/environment + +case "$1" in + sim) + bash scripts/linux/simulate.sh + ;; + wave) + bash scripts/linux/gtkwave.sh + ;; + clean) + bash scripts/linux/clean.sh + ;; + build) + bash scripts/linux/build.sh + ;; + *) + echo "Usage: $0 {sim|wave|clean|build}" + ;; +esac diff --git a/Semaine_6/UART_ULTRASON_COMMANDS/scripts/linux/build.sh b/Semaine_6/UART_ULTRASON_COMMANDS/scripts/linux/build.sh new file mode 100644 index 0000000..3c59fe2 --- /dev/null +++ b/Semaine_6/UART_ULTRASON_COMMANDS/scripts/linux/build.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +# Aller à la racine du projet +cd "$(dirname "$0")/../.." || exit 1 + +# Config de base +DEVICE="GW2AR-LV18QN88C8/I7" +BOARD="tangnano20k" +TOP="top_uart_ultrason_command" +CST_FILE="$TOP.cst" +JSON_FILE="runs/$TOP.json" +PNR_JSON="runs/pnr_$TOP.json" +BITSTREAM="runs/$TOP.fs" + +# Créer le dossier runs si nécessaire +mkdir -p runs + +echo "=== Étape 1 : Synthèse avec Yosys ===" +yosys -p "read_verilog -sv src/verilog/$TOP.v IP/verilog/ultrasonic_fpga.v IP/verilog/uart_tx_fifo.v IP/verilog/uart_rx_fifo.v IP/verilog/rxuartlite.v IP/verilog/fifo.v IP/verilog/uart_tx.v; synth_gowin -top $TOP -json $JSON_FILE" +if [ $? -ne 0 ]; then + echo "=== Erreur lors de la synthèse ===" + exit 1 +fi + +echo "=== Étape 2 : Placement & Routage avec nextpnr-himbaechel ===" +nextpnr-himbaechel --json "$JSON_FILE" --write "$PNR_JSON" --device "$DEVICE" --vopt cst=constraints/"$CST_FILE" --vopt family=GW2A-18C +if [ $? -ne 0 ]; then + echo "=== Erreur lors du placement/routage ===" + exit 1 +fi + +echo "=== Étape 3 : Packing avec gowin_pack ===" +gowin_pack -d "$DEVICE" -o "$BITSTREAM" "$PNR_JSON" +if [ $? -ne 0 ]; then + echo "=== Erreur lors du packing ===" + exit 1 +fi + +echo "=== Étape 4 : Flash avec openFPGALoader ===" +openFPGALoader -b "$BOARD" "$BITSTREAM" +if [ $? -ne 0 ]; then + echo "=== Erreur lors du flash ===" + exit 1 +fi + +echo "=== Compilation et flash réussis ===" diff --git a/Semaine_6/UART_ULTRASON_COMMANDS/scripts/linux/clean.sh b/Semaine_6/UART_ULTRASON_COMMANDS/scripts/linux/clean.sh new file mode 100644 index 0000000..a505cb7 --- /dev/null +++ b/Semaine_6/UART_ULTRASON_COMMANDS/scripts/linux/clean.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +echo "=== Nettoyage des fichiers générés ===" +rm -rf runs/* diff --git a/Semaine_6/UART_ULTRASON_COMMANDS/scripts/linux/gtkwave.sh b/Semaine_6/UART_ULTRASON_COMMANDS/scripts/linux/gtkwave.sh new file mode 100644 index 0000000..3c97f71 --- /dev/null +++ b/Semaine_6/UART_ULTRASON_COMMANDS/scripts/linux/gtkwave.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +echo "=== Lancement de GTKWave ===" +gtkwave runs/ultrason_commands.vcd +echo "=== GTKWave terminé ===" \ No newline at end of file diff --git a/Semaine_6/UART_ULTRASON_COMMANDS/scripts/linux/simulate.sh b/Semaine_6/UART_ULTRASON_COMMANDS/scripts/linux/simulate.sh new file mode 100644 index 0000000..72e0421 --- /dev/null +++ b/Semaine_6/UART_ULTRASON_COMMANDS/scripts/linux/simulate.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +echo "=== Simulation avec Icarus Verilog ===" + +OUT="runs/sim.vvp" +TOP="tb_ultrason_commands" +DIRS=("src/verilog" "tests/verilog" "IP/verilog") + +FILES=() +for dir in "${DIRS[@]}"; do + for file in "$dir"/*.v; do + FILES+=("$file") + done +done + +iverilog -g2012 -o "$OUT" -s "$TOP" "${FILES[@]}" +vvp "$OUT" diff --git a/Semaine_6/UART_ULTRASON_COMMANDS/scripts/windows/build.bat b/Semaine_6/UART_ULTRASON_COMMANDS/scripts/windows/build.bat new file mode 100644 index 0000000..8182a70 --- /dev/null +++ b/Semaine_6/UART_ULTRASON_COMMANDS/scripts/windows/build.bat @@ -0,0 +1,45 @@ +@echo off +setlocal + +rem === Aller à la racine du projet === +cd /d %~dp0\..\.. + +rem === Config de base === +set DEVICE=GW2AR-LV18QN88C8/I7 +set BOARD=tangnano20k +set TOP=top_uart_ultrason_command +set CST_FILE=%TOP%.cst +set JSON_FILE=runs/%TOP%.json +set PNR_JSON=runs/pnr_%TOP%.json +set BITSTREAM=runs/%TOP%.fs + +rem === Créer le dossier runs si nécessaire === +if not exist runs ( + mkdir runs +) + +echo === Étape 1 : Synthèse avec Yosys === +yosys -p "read_verilog -sv src/verilog/%TOP%.v IP/verilog/ultrasonic_fpga.v IP/verilog/uart_tx_fifo.v IP/verilog/uart_rx_fifo.v IP/verilog/rxuartlite.v IP/verilog/txuartlite.v IP/verilog/fifo.v; synth_gowin -top %TOP% -json %JSON_FILE%" +if errorlevel 1 goto error + +echo === Étape 2 : Placement & Routage avec nextpnr-himbaechel === +nextpnr-himbaechel --json %JSON_FILE% --write %PNR_JSON% --device %DEVICE% --vopt cst=constraints/%CST_FILE% --vopt family=GW2A-18C +if errorlevel 1 goto error + +echo === Étape 3 : Packing avec gowin_pack === +gowin_pack -d %DEVICE% -o %BITSTREAM% %PNR_JSON% +if errorlevel 1 goto error + +echo === Étape 4 : Flash avec openFPGALoader === +openFPGALoader -b %BOARD% %BITSTREAM% +if errorlevel 1 goto error + +echo === Compilation et flash réussis === +goto end + +:error +echo === Une erreur est survenue === + +:end +endlocal +pause diff --git a/Semaine_6/UART_ULTRASON_COMMANDS/scripts/windows/clean.bat b/Semaine_6/UART_ULTRASON_COMMANDS/scripts/windows/clean.bat new file mode 100644 index 0000000..6192ae1 --- /dev/null +++ b/Semaine_6/UART_ULTRASON_COMMANDS/scripts/windows/clean.bat @@ -0,0 +1,4 @@ +@echo off +echo === Nettoyage du dossier runs === +rd /s /q runs +mkdir runs diff --git a/Semaine_6/UART_ULTRASON_COMMANDS/scripts/windows/gtkwave.bat b/Semaine_6/UART_ULTRASON_COMMANDS/scripts/windows/gtkwave.bat new file mode 100644 index 0000000..a9771b8 --- /dev/null +++ b/Semaine_6/UART_ULTRASON_COMMANDS/scripts/windows/gtkwave.bat @@ -0,0 +1,3 @@ +@echo off +echo === Lancement de GTKWave === +gtkwave runs/ultrason_commands.vcd diff --git a/Semaine_6/UART_ULTRASON_COMMANDS/scripts/windows/simulate.bat b/Semaine_6/UART_ULTRASON_COMMANDS/scripts/windows/simulate.bat new file mode 100644 index 0000000..f238785 --- /dev/null +++ b/Semaine_6/UART_ULTRASON_COMMANDS/scripts/windows/simulate.bat @@ -0,0 +1,29 @@ +@echo off +echo === Simulation avec Icarus Verilog === +setlocal enabledelayedexpansion + +:: Dossier de sortie +set OUT=runs/sim.vvp + +:: Top-level testbench module +set TOP=tb_ultrason_commands + +:: Répertoires contenant des fichiers .v +set DIRS=src/verilog tests/verilog IP/verilog + +:: Variable pour stocker les fichiers +set FILES= + +:: Boucle sur chaque dossier +for %%D in (%DIRS%) do ( + for %%F in (%%D\*.v) do ( + set FILES=!FILES! %%F + ) +) + +:: Compilation avec Icarus Verilog +iverilog -g2012 -o %OUT% -s %TOP% %FILES% + +endlocal + +vvp runs/sim.vvp \ No newline at end of file diff --git a/Semaine_6/UART_ULTRASON_COMMANDS/src/verilog/top_uart_ultrason_command.v b/Semaine_6/UART_ULTRASON_COMMANDS/src/verilog/top_uart_ultrason_command.v new file mode 100644 index 0000000..eb56ab0 --- /dev/null +++ b/Semaine_6/UART_ULTRASON_COMMANDS/src/verilog/top_uart_ultrason_command.v @@ -0,0 +1,177 @@ +module top_uart_ultrason_command ( + input wire clk, // 27 MHz + output wire tx, + input wire rx, + inout wire ultrason_sig, // Capteur ultrason + output reg [5:0] leds +); + + // === UART RX WIRE === + wire [7:0] rd_data; + reg rd_en = 0; + wire data_available; + + // RX FIFO Instance + uart_rx_fifo uart_rx_inst ( + .clk(clk), + .rx_pin(rx), + .rd_data(rd_data), + .rd_en(rd_en), + .data_available(data_available) + ); + + // === UART TX WIRE === + reg [7:0] wr_data; + reg wr_en; + wire tx_fifo_full; + + // === UART TX FIFO === + uart_tx_fifo uart_tx_inst ( + .clk(clk), + .wr_en(wr_en), + .wr_data(wr_data), + .fifo_full(tx_fifo_full), + .tx_pin(tx) + ); + + // === Ultrasonic === + reg start = 0; + wire ultrasonic_busy; + wire [15:0] distance; + wire ultrason_done; + + ultrasonic_fpga #( + .CLK_FREQ(27_000_000) + ) ultrasonic_inst ( + .clk(clk), + .start(start), + .sig(ultrason_sig), + .distance(distance), + .busy(ultrasonic_busy), + .done(ultrason_done) + ); + + // === FSM === + localparam IDLE = 0, ONESTART = 1, ONESTOP = 2, CONTINUOUSSTART = 3, CONTINUOUSSTOP = 4, WAIT = 5, NEXT_FIFO = 6; + + reg [1:0] command = 0; + + reg [31:0] delay_counter = 0; + + localparam US_STATE_WIDTH = $clog2(NEXT_FIFO)+1; + reg [US_STATE_WIDTH-1:0] mesure_state = IDLE; + + always @(posedge clk) begin + if (data_available) begin + command <= rd_data[1:0]; + leds <= rd_data[7:2]; + end + end + + always @(posedge clk) begin // Mesure state machine + case (mesure_state) + IDLE: begin + if (command == 2'd1 && data_available) begin + mesure_state <= ONESTART; + rd_en <= 1; + end else if (command == 2'd2 && data_available) begin + mesure_state <= CONTINUOUSSTART; + rd_en <= 1; + end else begin + mesure_state <= IDLE; + rd_en <= 0; + end + end + + ONESTART: begin + start <= 1; + mesure_state <= ONESTOP; + rd_en <= 0; + end + + ONESTOP: begin + start <= 0; + mesure_state <= IDLE; + end + + CONTINUOUSSTART: begin + if (command == 3) begin + mesure_state <= NEXT_FIFO; + rd_en <= 1; + end else begin + mesure_state <= CONTINUOUSSTOP; + start <= 1; + rd_en <= 0; + end + end + + CONTINUOUSSTOP: begin + start <= 0; + mesure_state <= WAIT; + end + + WAIT: begin // Compteur 0.5s + if (delay_counter > 1) begin + delay_counter <= delay_counter - 1; + end else begin + mesure_state <= CONTINUOUSSTART; + delay_counter <= 13500000; + end + end + + NEXT_FIFO: begin + rd_en <= 1; + mesure_state <= IDLE; + end + + endcase + end + + localparam BUSY = 1, SEND_LOW = 2, SEND_HIGH = 3, DONE = 4; + reg [1:0] saver_state = IDLE; + always @(posedge clk) begin // FSM Pour enregistrer la distance + case (saver_state) + IDLE: begin + wr_en <= 0; + if (ultrasonic_busy) begin + saver_state <= BUSY; + end else begin + saver_state <= IDLE; + end + end + + BUSY: begin + if (ultrason_done) begin + saver_state <= SEND_LOW; + wr_en <= 1; + wr_data <= distance[7:0]; + end else if(ultrasonic_busy) begin + saver_state <= BUSY; + end else begin + saver_state <= IDLE; + end + end + + SEND_LOW: begin + wr_en <= 1; + wr_data <= distance[15:8]; + saver_state <= SEND_HIGH; + + end + + SEND_HIGH: begin + wr_en <= 0; + saver_state <= DONE; + end + + DONE: begin + wr_data <= 0; + wr_en <= 0; + saver_state <= IDLE; + end + + endcase + + end + +endmodule diff --git a/Semaine_6/UART_ULTRASON_COMMANDS/tests/Python/ultrason_command.py b/Semaine_6/UART_ULTRASON_COMMANDS/tests/Python/ultrason_command.py new file mode 100644 index 0000000..2bdab1e --- /dev/null +++ b/Semaine_6/UART_ULTRASON_COMMANDS/tests/Python/ultrason_command.py @@ -0,0 +1,68 @@ +import serial +import time +import struct + +# === Paramètres de communication === +SERIAL_PORT = "COM6" # Modifie selon ton système, ex. "/dev/ttyUSB0" sur Linux +BAUDRATE = 115200 # Change si différent +TIMEOUT = 2 # secondes + +# === Commandes (doivent correspondre aux valeurs Verilog) === +CMD_STOP = 3 +CMD_ONE = 1 +CMD_CONTINUOUS = 2 + +def send_command(ser, command): + """Envoie une commande au FPGA""" + ser.write(bytes([command])) + +def read_distance(ser): + """Lit 2 octets et les convertit en distance (int16)""" + data = ser.read(2) + if len(data) != 2: + return None + # Interprétation little-endian (LSB, MSB) + lsb, msb = data[0], data[1] + distance = msb << 8 | lsb + return distance + +def main(): + with serial.Serial(SERIAL_PORT, BAUDRATE, timeout=TIMEOUT) as ser: + print("Connexion ouverte sur", SERIAL_PORT) + + mode = input("Mode (one / continuous / stop) ? ").strip().lower() + + if mode == "one": + send_command(ser, CMD_ONE) + print("Mesure unique demandée. Attente résultat...") + time.sleep(0.05) + distance = read_distance(ser) + if distance is not None: + print(f"Distance mesurée : {distance} cm") + else: + print("Erreur : distance non reçue.") + + elif mode == "continuous": + send_command(ser, CMD_CONTINUOUS) + print("Mesures continues (CTRL+C pour stopper) :") + try: + while True: + distance = read_distance(ser) + if distance is not None: + print(f"Distance : {distance} cm") + else: + print("... (aucune donnée reçue)") + time.sleep(0.1) + except KeyboardInterrupt: + send_command(ser, CMD_STOP) + print("\nMesure continue arrêtée.") + + elif mode == "stop": + send_command(ser, CMD_STOP) + print("Commande STOP envoyée.") + + else: + print("Commande invalide.") + +if __name__ == "__main__": + main() diff --git a/Semaine_6/UART_ULTRASON_COMMANDS/tests/verilog/tb_ultrason_commands.v b/Semaine_6/UART_ULTRASON_COMMANDS/tests/verilog/tb_ultrason_commands.v new file mode 100644 index 0000000..dedeead --- /dev/null +++ b/Semaine_6/UART_ULTRASON_COMMANDS/tests/verilog/tb_ultrason_commands.v @@ -0,0 +1,187 @@ +`timescale 1ns/1ps + +module tb_ultrason_commands; + + // === Signaux === + reg clk = 0; + always #18.5 clk = ~clk; // Horloge 27 MHz (période ~37ns) + + wire tx, rx; + wire [5:0] leds; + wire ultrason_sig; + + reg tx_enable = 0; + wire tx_ready; + reg [7:0] data_in = 8'h00; + wire [7:0] data_out; + wire data_available; + reg rd_en = 0; + + // === Paramètres === + localparam CLK_FREQ = 27_000_000; + localparam BAUD_RATE = 115_200; + localparam CLK_PERIOD = 37; // ns + + // === Module testé === + top_uart_ultrason_command dut ( + .clk(clk), + .rx(rx), + .tx(tx), + .ultrason_sig(ultrason_sig), + .leds(leds) + ); + + // === Simulation capteur ultrason === + // Supposons que fake_sensor fournit un signal 'distance' pour vérification + wire [15:0] sensor_distance; + ultrasonic_sensor fake_sensor ( + .clk(clk), + .signal(ultrason_sig), + ); + + // === RX FIFO pour observer la sortie UART === + uart_rx_fifo #( + .CLK_FREQ(CLK_FREQ), + .BAUD_RATE(BAUD_RATE) + ) uart_rx_fifo_inst ( + .clk(clk), + .rx_pin(tx), + .rd_en(rd_en), + .rd_data(data_out), + .data_available(data_available) + ); + + // === TX pour injecter des commandes UART === + uart_tx #( + .CLK_FREQ(CLK_FREQ), + .BAUD_RATE(BAUD_RATE) + ) uart_tx_inst ( + .clk(clk), + .tx_enable(tx_enable), + .tx_ready(tx_ready), + .data(data_in), + .tx(rx), + .rst_p(1'b0) + ); + + // === Tâches pour simplifier les tests === + task send_command(input [7:0] cmd); + begin + wait(tx_ready); + $display("[%0t ns] Envoi commande: %0d", $time, cmd); + data_in = cmd; + tx_enable = 1; + #(CLK_PERIOD * 2); + tx_enable = 0; + end + endtask + + task read_distance(output [15:0] distance); + reg [7:0] lsb, msb; + begin + // Attendre premier octet (LSB) + wait(data_available); + #(CLK_PERIOD); + rd_en = 1; + #(CLK_PERIOD); + lsb = data_out; + rd_en = 0; + $display("[%0t ns] Reçu octet LSB: %0d", $time, lsb); + + // Attendre second octet (MSB) + wait(data_available); + #(CLK_PERIOD); + rd_en = 1; + #(CLK_PERIOD); + msb = data_out; + rd_en = 0; + $display("[%0t ns] Reçu octet MSB: %0d", $time, msb); + + distance = {msb, lsb}; + end + endtask + + task check_leds(input [7:0] cmd); + begin + #(CLK_PERIOD * 10); // Attendre mise à jour des LEDs + if (leds !== cmd[7:2]) begin + $display("[%0t ns] ERREUR: LEDs=%b, attendu=%b", $time, leds, cmd[7:2]); + end else begin + $display("[%0t ns] LEDs correctes: %b", $time, leds); + end + end + endtask + + // === Séquence de test === + initial begin + $dumpfile("runs/ultrason_commands.vcd"); + $dumpvars(0, tb_ultrason_commands); + + $display("==== Début Test UART Ultrason ===="); + + // Initialisation + #(CLK_PERIOD * 10); + + // Test 1: Commande ONE (8'd1) + $display("=== Test 1: Commande ONE ==="); + send_command(8'd1); // Commande: ONE + check_leds(8'd1); + begin + reg [15:0] received_distance; + read_distance(received_distance); + if (received_distance == sensor_distance) begin + $display("[%0t ns] Distance correcte: %0d", $time, received_distance); + end else begin + $display("[%0t ns] ERREUR: Distance reçue=%0d, attendu=%0d", $time, received_distance, sensor_distance); + end + end + + // Test 2: Commande CONTINUOUS (8'd2) + $display("=== Test 2: Commande CONTINUOUS ==="); + send_command(8'd2); // Commande: CONTINUOUS + check_leds(8'd2); + repeat (3) begin // Lire 3 mesures consécutives + reg [15:0] received_distance; + read_distance(received_distance); + if (received_distance == sensor_distance) begin + $display("[%0t ns] Distance continue correcte: %0d", $time, received_distance); + end else begin + $display("[%0t ns] ERREUR: Distance reçue=%0d, attendu=%0d", $time, received_distance, sensor_distance); + end + #(CLK_PERIOD * 13500000); // Attendre ~0.5s (délai dans WAIT) + end + + // Test 3: Commande STOP (8'd3) + $display("=== Test 3: Commande STOP ==="); + send_command(8'd3); // Commande: STOP + check_leds(8'd3); + #(CLK_PERIOD * 13500000); // Attendre pour vérifier l'arrêt + + if (data_available) begin + $display("[%0t ns] Vérification STOP: aucune donnée ne doit être reçue", $time); + #(CLK_PERIOD * 1000); + if (data_available) begin + $display("[%0t ns] ERREUR: Données reçues après STOP", $time); + end else begin + $display("[%0t ns] STOP correct: aucune donnée reçue", $time); + end + end + + // Test 4: Commande invalide (8'd4) + $display("=== Test 4: Commande invalide ==="); + send_command(8'd4); // Commande invalide + #(CLK_PERIOD * 1000); + if (data_available) begin + $display("[%0t ns] ERREUR: Données reçues pour commande invalide", $time); + end else begin + $display("[%0t ns] Commande invalide ignorée correctement", $time); + end + + // Fin de la simulation + $display("==== Fin Test UART Ultrason ===="); + #(CLK_PERIOD * 1000); + $stop; + end + + +endmodule \ No newline at end of file