aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--atk16_fpga/top.v179
-rw-r--r--atk16_fpga/uart_rx.v87
-rw-r--r--atk16_fpga/uart_tx.v46
3 files changed, 191 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
diff --git a/atk16_fpga/uart_rx.v b/atk16_fpga/uart_rx.v
new file mode 100644
index 0000000..5d6129e
--- /dev/null
+++ b/atk16_fpga/uart_rx.v
@@ -0,0 +1,87 @@
+module uart_rx (
+ input wire clk, // System clock: 100 MHz
+ input wire rx, // Serial RX line
+ output reg [7:0] data, // Received data byte
+ output reg done // Goes high for one clock cycle when a frame is received
+);
+
+ // CLK / baudrate cycles per bit.
+ //parameter CLKS_PER_BIT = 10417; // 9600 baud rate
+ parameter CLKS_PER_BIT = 868; // 115200 baud rate
+
+ // Define states for the state machine.
+ localparam STATE_IDLE = 2'd0;
+ localparam STATE_START = 2'd1;
+ localparam STATE_DATA = 2'd2;
+ localparam STATE_STOP = 2'd3;
+
+ reg [ 1:0] state = STATE_IDLE;
+ reg [13:0] clk_count = 14'd0;
+ reg [ 2:0] bit_index = 3'd0; // Will count 0 to 7 for the 8 data bits.
+ reg [ 7:0] rx_shift_reg = 8'd0;
+
+ // Synchronous state machine.
+ always @(posedge clk) begin
+ case (state)
+ STATE_IDLE: begin
+ done <= 1'b0;
+ clk_count <= 14'd0;
+ bit_index <= 3'd0;
+ if (rx == 1'b0) begin
+ // Detected start bit. Go to START state.
+ state <= STATE_START;
+ end else begin
+ state <= STATE_IDLE;
+ end
+ end
+
+ STATE_START: begin
+ // Wait for half a bit period, then sample the start bit.
+ if (clk_count == (CLKS_PER_BIT - 1) / 2) begin
+ // Sample start bit: it must be 0.
+ if (rx == 1'b0) begin
+ clk_count <= 14'd0;
+ state <= STATE_DATA;
+ end else begin
+ // False start; return to idle.
+ state <= STATE_IDLE;
+ end
+ end else begin
+ clk_count <= clk_count + 1;
+ end
+ end
+
+ STATE_DATA: begin
+ // Wait for a full bit period then sample the data bit.
+ if (clk_count < CLKS_PER_BIT - 1) begin
+ clk_count <= clk_count + 1;
+ end else begin
+ clk_count <= 14'd0;
+ // Sample the current data bit.
+ rx_shift_reg[bit_index] <= rx;
+ if (bit_index == 3'd7) begin
+ state <= STATE_STOP;
+ end else begin
+ bit_index <= bit_index + 1;
+ end
+ end
+ end
+
+ STATE_STOP: begin
+ // Wait one bit period for the stop bit.
+ if (clk_count < CLKS_PER_BIT - 1) begin
+ clk_count <= clk_count + 1;
+ end else begin
+ clk_count <= 14'd0;
+ // Optionally, you could check that rx is high (stop bit).
+ data <= rx_shift_reg; // Latch the received data.
+ done <= 1'b1; // Signal that a frame has been received.
+ state <= STATE_IDLE; // Go back to idle for the next frame.
+ end
+ end
+
+ default: state <= STATE_IDLE;
+ endcase
+ end
+
+endmodule
diff --git a/atk16_fpga/uart_tx.v b/atk16_fpga/uart_tx.v
new file mode 100644
index 0000000..a91c726
--- /dev/null
+++ b/atk16_fpga/uart_tx.v
@@ -0,0 +1,46 @@
+// 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 start, // one-cycle pulse to start transmission
+ input [7:0] data, // data byte to send
+ output reg tx = 1'b1, // serial TX line
+ output reg busy = 1'b0 // high while transmitting the byte
+);
+
+ // CLK / baudrate cycles per bit.
+ //parameter CLKS_PER_BIT = 10417; // 9600 baud rate
+ parameter CLKS_PER_BIT = 868; // 115200 baud rate
+
+ reg [13:0] clk_count = 14'b0; // counter for baud tick (14 bits is enough)
+ reg [ 3:0] bit_index = 4'b0; // 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 (!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
+endmodule