aboutsummaryrefslogtreecommitdiffstats
path: root/atk16_fpga/top.v
diff options
context:
space:
mode:
authorJan Tuomi <jan@jantuomi.fi>2025-03-18 22:34:54 +0200
committerJan Tuomi <jan@jantuomi.fi>2025-03-18 22:34:54 +0200
commit1294b00c0941085b1abe782be65edfe90dafb04e (patch)
treee8a7d31f328ddaf99ebe685c432ecdf291d5a4b2 /atk16_fpga/top.v
parent182c35d5aee98ab2afa27c2e84427206edb9a9ca (diff)
Fix UART
Diffstat (limited to 'atk16_fpga/top.v')
-rw-r--r--atk16_fpga/top.v179
1 files changed, 58 insertions, 121 deletions
diff --git a/atk16_fpga/top.v b/atk16_fpga/top.v
index df53491..c67ade2 100644
--- a/atk16_fpga/top.v
+++ b/atk16_fpga/top.v
@@ -1,157 +1,94 @@
-// UART transmitter module.
-// Transmits a frame: start bit (0), 8 data bits (LSB first), stop bit (1)
-// at a baud rate determined by CLKS_PER_BIT (here: 10417 for 100MHz/9600)
-module uart_tx (
- input clk, // system clock: 100 MHz
- input rst, // synchronous reset (active high)
- input start, // one-cycle pulse to start transmission
- input [7:0] data, // data byte to send
- output reg tx, // serial output
- output reg busy // high while transmitting the byte
-);
- // 100e6/9600 ≈ 10417 cycles per bit.
- parameter CLKS_PER_BIT = 10417;
-
- reg [13:0] clk_count; // counter for baud tick (14 bits is enough)
- reg [ 3:0] bit_index; // counts from 0 to 9 (10 bits total: start, 8 data, stop)
- reg [ 9:0] tx_frame; // complete frame: {stop bit, data[7:0], start bit}
-
- always @(posedge clk) begin
- if (rst) begin
- busy <= 1'b0;
- tx <= 1'b1; // idle state is high
- clk_count <= 0;
- bit_index <= 0;
- end else begin
- if (!busy) begin
- if (start) begin
- // Load frame: start bit (0), 8 data bits (LSB first), stop bit (1)
- tx_frame <= {1'b1, data, 1'b0};
- busy <= 1'b1;
- clk_count <= 0;
- bit_index <= 0;
- tx <= 1'b0; // send start bit immediately
- end else begin
- tx <= 1'b1; // remain idle
- end
- end else begin
- // When busy, count clocks for each bit period.
- if (clk_count < CLKS_PER_BIT - 1) clk_count <= clk_count + 1;
- else begin
- clk_count <= 0;
- bit_index <= bit_index + 1;
- if (bit_index < 9) tx <= tx_frame[bit_index+1];
- else begin
- busy <= 1'b0;
- tx <= 1'b1; // return to idle
- end
- end
- end
- end
- end
-endmodule
-
// Top-level module for the iCE40 HX8K FPGA.
// This module instantiates the UART transmitter and sends the bytes for
// "Hello" and a newline character, then stops.
module top (
input SYSCLK, // 100 MHz system clock
- input SERIAL_RX, // Unused in this design; available as a pin
- output SERIAL_TX // UART transmit output
+ input SERIAL_RX, // UART receive input
+ output SERIAL_TX, // UART transmit output
+ output LED1,
+ output LED2
);
- // Create a simple synchronous reset signal.
- // Here, we use a counter that holds reset high for a few hundred cycles.
- reg [7:0] reset_cnt;
- reg sys_rst;
- always @(posedge SYSCLK) begin
- if (reset_cnt != 8'd255) begin
- reset_cnt <= reset_cnt + 1;
- sys_rst <= 1'b1;
- end else begin
- sys_rst <= 1'b0;
- end
- end
-
- // ROM for the message "Hello\n" (6 bytes)
- reg [7:0] message[0:5];
- initial begin
- message[0] = "H"; // ASCII 72
- message[1] = "e"; // ASCII 101
- message[2] = "l"; // ASCII 108
- message[3] = "l"; // ASCII 108
- message[4] = "o"; // ASCII 111
- message[5] = "\n"; // ASCII 10 (newline)
- end
-
- // Pointer to index through the message.
- reg [2:0] index;
// One-cycle pulse to trigger transmission.
- reg start_tx;
+ reg start_tx = 1'b0;
// Data byte to be transmitted.
- reg [7:0] data_to_send;
+ reg [7:0] data_to_send = 8'b0;
// Busy flag from the transmitter.
wire tx_busy;
+ reg [7:0] letter_a = 8'd97;
+
// State machine states.
- localparam IDLE = 2'd0, WAIT_BUSY = 2'd1, WAIT_IDLE = 2'd2, DONE = 2'd3;
- reg [1:0] state;
+ localparam WAIT_RX_BUSY = 3'd0;
+ localparam WAIT_RX_DONE = 3'd1;
+ localparam START_TX_1 = 3'd2;
+ localparam START_TX_2 = 3'd3;
+ localparam WAIT_TX_DONE = 3'd4;
+
+ reg [2:0] state = WAIT_RX_BUSY;
// Synchronous state machine with reset.
always @(posedge SYSCLK) begin
- if (sys_rst) begin
- // Initialize on reset.
- state <= IDLE;
- index <= 0;
- start_tx <= 1'b0;
- data_to_send <= 8'd0;
- end else begin
- case (state)
- IDLE: begin
- start_tx <= 1'b0;
- if (index < 3'd6) begin
- if (!tx_busy) begin
- data_to_send <= message[index];
- start_tx <= 1'b1; // trigger transmission
- state <= WAIT_BUSY;
- end
- end else begin
- state <= DONE;
- end
+ case (state)
+ WAIT_RX_BUSY: begin
+ if (!rx_done) begin
+ state <= WAIT_RX_DONE;
end
+ end
- // After issuing the start pulse, wait for tx_busy to go high.
- WAIT_BUSY: begin
- start_tx <= 1'b0; // ensure pulse is only one cycle.
- if (tx_busy) state <= WAIT_IDLE;
+ WAIT_RX_DONE: begin
+ if (rx_done) begin
+ state <= START_TX_1;
end
+ end
- // Wait for the transmitter to finish sending the byte.
- WAIT_IDLE: begin
- if (!tx_busy) begin
- index <= index + 1; // move to next byte
- state <= IDLE;
- end
+ START_TX_1: begin
+ if (!tx_busy) begin
+ //data_to_send <= letter_a;
+ data_to_send <= data_to_receive;
+ start_tx <= 1'b1; // trigger transmission
+ state <= START_TX_2;
end
+ end
+
+ // After issuing the start pulse, wait for tx_busy to go high.
+ START_TX_2: begin
+ start_tx <= 1'b0; // ensure pulse is only one cycle.
+ state <= WAIT_TX_DONE;
+ end
- DONE: begin
- start_tx <= 1'b0;
- // Remain in DONE state; no further transmissions.
+ // Wait for the transmitter to finish sending the byte.
+ WAIT_TX_DONE: begin
+ if (!tx_busy) begin
+ state <= WAIT_RX_BUSY;
end
+ end
+
+ default: state <= WAIT_RX_BUSY;
+ endcase
- default: state <= IDLE;
- endcase
- end
end
// Instantiate the UART transmitter.
uart_tx uart_inst (
.clk (SYSCLK),
- .rst (sys_rst), // Use our synchronous reset signal.
.start(start_tx), // One-cycle pulse to start transmission.
.data (data_to_send),
.tx (SERIAL_TX),
.busy (tx_busy)
);
+ wire [7:0] data_to_receive;
+ wire rx_done;
+
+ uart_rx uart_rx_inst (
+ .clk (SYSCLK),
+ .rx (SERIAL_RX),
+ .data(data_to_receive),
+ .done(rx_done)
+ );
+
+ assign LED1 = data_to_receive == letter_a ? 1 : 0;
+ assign LED2 = LED1;
+
endmodule