diff --git a/Semaine_4/UART_ULTRASON/.gitignore b/Semaine_4/UART_ULTRASON/.gitignore new file mode 100644 index 0000000..33b5bda --- /dev/null +++ b/Semaine_4/UART_ULTRASON/.gitignore @@ -0,0 +1,4 @@ +runs +.vscode +workspace.code-workspace +*.pyc diff --git a/Semaine_4/UART_ULTRASON/IP/verilog/fifo.v b/Semaine_4/UART_ULTRASON/IP/verilog/fifo.v new file mode 100644 index 0000000..a67914e --- /dev/null +++ b/Semaine_4/UART_ULTRASON/IP/verilog/fifo.v @@ -0,0 +1,43 @@ + module fifo #( + parameter DEPTH = 16, + parameter WIDTH = 8 +)( + input wire clk, + input wire wr_en, + input wire[WIDTH-1:0] wr_data, + input wire rd_en, + output wire[WIDTH-1:0] rd_data, + + output wire full, + output wire empty +); + + reg [WIDTH-1:0] fifo[0:DEPTH-1]; + reg [3:0] wr_ptr; + reg [3:0] rd_ptr; + reg [3:0] count; + + assign full = (count == DEPTH); + assign empty = (count == 0); + assign rd_data = fifo[rd_ptr]; + + initial begin + wr_ptr = 0; + rd_ptr = 0; + count = 0; + end + + always @(posedge clk) begin + if (wr_en && !full) begin + fifo[wr_ptr] <= wr_data; + wr_ptr <= (wr_ptr + 1) % DEPTH; + count <= count + 1; + end + + if (rd_en && !empty) begin + rd_ptr <= (rd_ptr + 1) % DEPTH; + count <= count - 1; + end + end + +endmodule diff --git a/Semaine_4/UART_ULTRASON/IP/verilog/uart_tx.v b/Semaine_4/UART_ULTRASON/IP/verilog/uart_tx.v new file mode 100644 index 0000000..9f48d93 --- /dev/null +++ b/Semaine_4/UART_ULTRASON/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_4/UART_ULTRASON/IP/verilog/uart_tx_fifo.v b/Semaine_4/UART_ULTRASON/IP/verilog/uart_tx_fifo.v new file mode 100644 index 0000000..80ebb84 --- /dev/null +++ b/Semaine_4/UART_ULTRASON/IP/verilog/uart_tx_fifo.v @@ -0,0 +1,86 @@ +module uart_tx_fifo #( + parameter CLK_FREQ = 27_000_000, + parameter BAUD_RATE = 115200, + parameter FIFO_DEPTH = 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_ready; + reg uart_tx_enable; + reg [7:0] uart_tx_data; + + // FSM + typedef enum logic [1:0] { + IDLE, + WAIT_READY, + SEND + } state_t; + + state_t state = IDLE; + + // FIFO instantiation + fifo #( + .WIDTH(8), + .DEPTH(FIFO_DEPTH) + ) 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 + uart_tx #( + .CLK_FREQ(CLK_FREQ), + .BAUD_RATE(BAUD_RATE) + ) uart_tx_inst ( + .clk(clk), + .rst_p(1'b0), + .data(uart_tx_data), + .tx_enable(uart_tx_enable), + .tx_ready(tx_ready), + .tx(tx_pin) + ); + + always_ff @(posedge clk) begin + // Valeurs par défaut + fifo_rd_en <= 0; + uart_tx_enable <= 0; + + case (state) + IDLE: begin + if (!fifo_empty) begin + state <= WAIT_READY; + end + end + + WAIT_READY: begin + if (tx_ready) begin + fifo_rd_en <= 1; + state <= SEND; + end + end + + SEND: begin + uart_tx_data <= fifo_rd_data; + uart_tx_enable <= 1; + state <= IDLE; + end + endcase + end + +endmodule \ No newline at end of file diff --git a/Semaine_4/UART_ULTRASON/IP/verilog/ultrasonic_fpga.v b/Semaine_4/UART_ULTRASON/IP/verilog/ultrasonic_fpga.v new file mode 100644 index 0000000..fdf4280 --- /dev/null +++ b/Semaine_4/UART_ULTRASON/IP/verilog/ultrasonic_fpga.v @@ -0,0 +1,150 @@ +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; + + reg [2:0] state = IDLE; + + 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; + + 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 + 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_4/UART_ULTRASON/constraints/top_uart_ultrason.cst b/Semaine_4/UART_ULTRASON/constraints/top_uart_ultrason.cst new file mode 100644 index 0000000..507b900 --- /dev/null +++ b/Semaine_4/UART_ULTRASON/constraints/top_uart_ultrason.cst @@ -0,0 +1,9 @@ +IO_LOC "tx" 69; +IO_PORT "tx" 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 "sig" 73; +IO_PORT "sig" IO_TYPE=LVCMOS33 PULL_MODE=UP DRIVE=8 BANK_VCCIO=3.3; + diff --git a/Semaine_4/UART_ULTRASON/project.bat b/Semaine_4/UART_ULTRASON/project.bat new file mode 100644 index 0000000..6998748 --- /dev/null +++ b/Semaine_4/UART_ULTRASON/project.bat @@ -0,0 +1,6 @@ +@call c:\oss-cad-suite\environment.bat +@echo off +if "%1"=="sim" call scripts\simulate.bat +if "%1"=="wave" call scripts\gtkwave.bat +if "%1"=="clean" call scripts\clean.bat +if "%1"=="build" call scripts\build.bat diff --git a/Semaine_4/UART_ULTRASON/scripts/build.bat b/Semaine_4/UART_ULTRASON/scripts/build.bat new file mode 100644 index 0000000..c4a0fcf --- /dev/null +++ b/Semaine_4/UART_ULTRASON/scripts/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 +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/fifo.v IP/verilog/uart_tx.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_4/UART_ULTRASON/scripts/clean.bat b/Semaine_4/UART_ULTRASON/scripts/clean.bat new file mode 100644 index 0000000..6192ae1 --- /dev/null +++ b/Semaine_4/UART_ULTRASON/scripts/clean.bat @@ -0,0 +1,4 @@ +@echo off +echo === Nettoyage du dossier runs === +rd /s /q runs +mkdir runs diff --git a/Semaine_4/UART_ULTRASON/scripts/gtkwave.bat b/Semaine_4/UART_ULTRASON/scripts/gtkwave.bat new file mode 100644 index 0000000..7388f4c --- /dev/null +++ b/Semaine_4/UART_ULTRASON/scripts/gtkwave.bat @@ -0,0 +1,3 @@ +@echo off +echo === Lancement de GTKWave === +gtkwave runs/uart_rx_fifo.vcd diff --git a/Semaine_4/UART_ULTRASON/scripts/simulate.bat b/Semaine_4/UART_ULTRASON/scripts/simulate.bat new file mode 100644 index 0000000..439cda3 --- /dev/null +++ b/Semaine_4/UART_ULTRASON/scripts/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_uart_rx_fifo + +:: 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_4/UART_ULTRASON/src/verilog/top_uart_ultrason.v b/Semaine_4/UART_ULTRASON/src/verilog/top_uart_ultrason.v new file mode 100644 index 0000000..5b1f3eb --- /dev/null +++ b/Semaine_4/UART_ULTRASON/src/verilog/top_uart_ultrason.v @@ -0,0 +1,70 @@ +module top_uart_ultrason ( + input wire clk, // 27 MHz + output wire tx, + inout wire sig, // Capteur ultrason +); + + + // === 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 done; + + ultrasonic_fpga #( + .CLK_FREQ(27_000_000) + ) ultrasonic_inst ( + .clk(clk), + .start(start), + .sig(sig), + .distance(distance), + .busy(ultrasonic_busy), + .done(done) + ); + + // === FSM === + localparam IDLE = 0, SEND_LOW = 2, SEND_HIGH = 3; + reg [1:0] state = IDLE; + + always @(posedge clk) begin + // Activer en continu tant que FIFO pas pleine + start <= 1; + + case (state) + IDLE: begin + wr_en <= 0; + if (done) begin + state <= SEND_LOW; + wr_en <= 1; + end + end + + SEND_LOW: begin + wr_en <= 1; + wr_data <= distance[7:0]; // Octet LSB + state <= IDLE; + end + + SEND_HIGH: begin + wr_data <= distance[15:8]; // Octet MSB + state <= IDLE; + end + + endcase + end + +endmodule diff --git a/Semaine_4/UART_ULTRASON/tests/Python/uart_ultrason_receiver.py b/Semaine_4/UART_ULTRASON/tests/Python/uart_ultrason_receiver.py new file mode 100644 index 0000000..a80d114 --- /dev/null +++ b/Semaine_4/UART_ULTRASON/tests/Python/uart_ultrason_receiver.py @@ -0,0 +1,21 @@ +import serial + +# === Configuration === +PORT = 'COM7' # Remplace par le port série de ton FPGA (ex: '/dev/ttyUSB0' sur Linux) +BAUDRATE = 115200 # À adapter selon ton uart_tx_fifo +TIMEOUT = 1 # En secondes + +# === Connexion série === +ser = serial.Serial(PORT, BAUDRATE, timeout=TIMEOUT) +print(f"Ouvert sur {PORT} à {BAUDRATE} bauds.") + +try: + while True: + data = ser.read(1) # Lire 1 octet + if data: + value = int.from_bytes(data, byteorder='little') + print(f"Distance mesurée : {value} cm") +except KeyboardInterrupt: + print("\nArrêté par l'utilisateur.") +finally: + ser.close() \ No newline at end of file diff --git a/Semaine_4/UART_ULTRASON/tests/verilog/tb_uart_ultrason.v b/Semaine_4/UART_ULTRASON/tests/verilog/tb_uart_ultrason.v new file mode 100644 index 0000000..0788abc --- /dev/null +++ b/Semaine_4/UART_ULTRASON/tests/verilog/tb_uart_ultrason.v @@ -0,0 +1,84 @@ +`timescale 1ns/1ps + +module tb_uart; + + reg clk = 0; + reg tx_enable = 0; + reg tx_ready; + reg [7:0] data_in = 8'h00; + reg [7:0] data_out; + + reg rx_received; + wire rx_enable = 1'b1; + + wire pin; + + always #18.5 clk = ~clk; + + localparam CLK_FREQ = 27_000_000; + localparam BAUD_RATE = 115_200; + + uart_rx #( + .CLK_FREQ(CLK_FREQ), + .BAUD_RATE(BAUD_RATE) + ) rx_instance ( + .clk(clk), + .rx_pin(pin), + .rx_data(data_out), + .rx_received(rx_received), + .rx_enable(rx_enable) + ); + + uart_tx #( + .CLK_FREQ(CLK_FREQ), + .BAUD_RATE(BAUD_RATE) + )tx_instance ( + .clk(clk), + .tx_enable(tx_enable), + .tx_ready(tx_ready), + .data(data_in), + .tx(pin), + .rst_p(1'b0) + ); + + initial begin + $dumpfile("runs/uart.vcd"); + $dumpvars(0, tb_uart); + + $display("======== Start UART LOOPBACK test ========="); + + #100; + + data_in <= 8'd234; // 234 + tx_enable <= 1; + wait(tx_ready == 1'b0); + tx_enable <= 0; + + // Attendre + wait (rx_received == 1'b1); // Attendre que le signal de reception soit actif + + $display("Data received: %d", data_out); // Afficher la valeur recu + $display("Data expected: %d", data_in); // Afficher la valeur envoyee + + #1000; + + wait(tx_ready == 1'b1); // Attendre que le signal de reception soit actif + + data_in <= 8'd202; // 202 + tx_enable <= 1; + wait(tx_ready == 1'b0); + tx_enable <= 0; + + // Attendre + wait (rx_received == 1'b1); // Attendre que le signal de reception soit actif + + $display("Data received: %d", data_out); // Afficher la valeur recu + $display("Data expected: %d", data_in); // Afficher la valeur envoyee + + $display("======== END UART TX test ========="); + + #1000; + $stop; + end + +endmodule \ No newline at end of file