diff options
| author | Jan Tuomi <jan@jantuomi.fi> | 2025-03-18 18:38:44 +0200 |
|---|---|---|
| committer | Jan Tuomi <jan@jantuomi.fi> | 2025-03-18 18:38:44 +0200 |
| commit | 69de37cdb25563f6cbd2a3c1a338b1e0dd47b3a4 (patch) | |
| tree | c1895044fd0051f919a989460be3ac39a6ab09b8 /atk16_fpga/top.v | |
| parent | 06e8b8dbadf769507abf5368caee253ded139adc (diff) | |
Start reworking fpga impl
Diffstat (limited to 'atk16_fpga/top.v')
| -rw-r--r-- | atk16_fpga/top.v | 175 |
1 files changed, 147 insertions, 28 deletions
diff --git a/atk16_fpga/top.v b/atk16_fpga/top.v index 065d034..df53491 100644 --- a/atk16_fpga/top.v +++ b/atk16_fpga/top.v @@ -1,38 +1,157 @@ -`default_nettype none - -`define PH_COPY_IMG 1'b0 -`define PH_RUN_IMG 1'b1 +// 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; -module top( - input wire SYSCLK, - input wire BUT1, - output wire LED1, + 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} - output SRAM_CS, // inverted - output SRAM_OE, // inverted - output SRAM_WE, // inverted - output [17:0] SA, - inout [15:0] SD, + 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 - input [3:0] INT +// 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 ); - reg phase = `PH_COPY_IMG; - cu cu_inst( - .clk(SYSCLK), - .rst(BUT1), + // 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 - .sram_cs_n(SRAM_CS), - .sram_rd_n(SRAM_OE), - .sram_wr_n(SRAM_WE), - .sram_addr(SA[15:0]), - .sram_in(SD), - .sram_out(SD), + // 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 - .int_lines(INT) - ); + // Pointer to index through the message. + reg [2:0] index; + // One-cycle pulse to trigger transmission. + reg start_tx; + // Data byte to be transmitted. + reg [7:0] data_to_send; + // Busy flag from the transmitter. + wire tx_busy; + + // State machine states. + localparam IDLE = 2'd0, WAIT_BUSY = 2'd1, WAIT_IDLE = 2'd2, DONE = 2'd3; + reg [1:0] state; + + // 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 + end - assign SA[17:16] = 2'b0; - assign LED1 = BUT1; + // 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; + 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 + end + + DONE: begin + start_tx <= 1'b0; + // Remain in DONE state; no further transmissions. + end + + 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) + ); endmodule |
