aboutsummaryrefslogtreecommitdiffstats
path: root/atk16_fpga/top.v
blob: df53491815c2777c95422df624eeb0eb07cb6538 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// 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
);

    // 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;
    // 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

                // 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