Search code examples
verilogsimulationstate-machinesynthesis

Verilog: Simulation and Hardware Implementation differ in one state behaviour


I am currently trying to implement a UART-simulation into an FPGA. The idea is, to just cycle through predefined data (defined by byte_array_com_message_1 - 4) which will then be put out on one line (out_cross_com1). Every byte shall be sent according to a used UART standard, so by keeping the signal high while idle, set a start bit by pulling low, send 8 bit of data, send a stop bit by pulling high, then start the next frame by pulling low etc.. My toolchain is verilog, vvp, gtkwave. EDIT: The synthesize is done by IceStudio for a Nandland GO Board. The code I have written looks fine in gtkwave. Gtk wave Simulation. Blue Highlight: Stop bit just 1 cycle, followed by the next start-bit But the oscilloscope screenshot shows, that two cycles of stop-bits are being output. Oscilloscope. First falling edge is the start-bit Both, simulation and oscilloscope show the first two bytes to transmit which are 0xA5 and 0x33 from [0:7]. Since I am quite new to verilog and FPGAs, I don't know how to debug this error systematically.

Code Under test:

wire out_cross_com1;
wire state_out_0;
wire state_out_1;

wire [7:0] byte_array_com_message_1 [0:31];
wire [7:0] byte_array_com_message_2 [0:31];
wire [7:0] byte_array_com_message_3 [0:31];
wire [7:0] byte_array_com_message_4 [0:31];
reg [2:0] bit_position;
reg [4:0] byte_position;
reg [1:0] message_index;
reg [1:0] uart_statemachine;
reg [1:0] uart_statemachine_next;
reg signal_out_cross_com1;

assign byte_array_com_message_1[0] = 8'hA5;
assign byte_array_com_message_1[1] = 8'h33;
//assign byte_array_com_message_1[2} = ....;

localparam
    uart_idle = 2'b00,
    uart_start = 2'b01,
    uart_transmit = 2'b10,
    uart_stop = 2'b11;

localparam
    message_1 = 2'b00,
    message_2 = 2'b01,
    message_3 = 2'b10,
    message_4 = 2'b11;

assign clk_out = clk;
assign out_cross_com1 = signal_out_cross_com1;
assign state_out_0 = uart_statemachine[0];
assign state_out_1 = uart_statemachine[1];

// output
always @(posedge clk)
begin
    if(rst == 1) begin
        signal_out_cross_com1 <= 1;
    end else begin
        case(uart_statemachine)
            uart_idle:
            begin
                signal_out_cross_com1 <= 1;
            end
            uart_start:
            begin
                signal_out_cross_com1 <= 0;
            end
            uart_transmit:
                case(message_index)
                    message_1:
                    begin
                        signal_out_cross_com1 <= byte_array_com_message_1[byte_position][bit_position];
                    end
                    message_2:
                    begin
                        signal_out_cross_com1 <= byte_array_com_message_2[byte_position][bit_position];
                    end
                    message_3:
                    begin
                        signal_out_cross_com1 <= byte_array_com_message_3[byte_position][bit_position];
                    end
                    message_4:
                    begin
                        signal_out_cross_com1 <= byte_array_com_message_4[byte_position][bit_position];
                    end
                endcase
            uart_stop:
            begin
                signal_out_cross_com1 <= 1;
            end
        endcase
    end

end

// update bit position
always @(posedge clk)
begin
    if(rst == 1) begin
        bit_position <= 3'b000;
        byte_position <= 5'b00000;
        message_index <= 2'b00;
    end else begin
        case(uart_statemachine)
            uart_idle:
            begin
                bit_position <= 3'b000;
                byte_position <= 5'b00000;
                message_index <= 2'b00;
            end
            uart_start:
            begin
                bit_position <= 3'b000;
                byte_position <= byte_position;
                message_index <= message_index;
            end
            uart_transmit:
            begin
                if(bit_position == 7) begin
                    bit_position <= 3'b000;
                    if(byte_position == 31) begin
                        byte_position <= 5'b00000;
                        if(message_index == 3) begin
                            message_index <= 2'b00;
                        end else begin
                            message_index <= message_index + 1;
                        end
                    end else begin
                        byte_position <= byte_position + 1;
                    end
                end else begin
                    bit_position <= (bit_position + 1);
                end
            end
            uart_stop:
            begin
                bit_position <= 3'b000;
                byte_position <= byte_position;
                message_index <= message_index;
            end
        endcase
    end
end

// Statemachine
always @(posedge clk)
begin
    if(rst == 1) begin
        uart_statemachine <= uart_idle;
    end else begin
        uart_statemachine <= uart_statemachine_next;
    end
end

// Determine next state
always @(posedge clk)
begin
    if(rst == 1) begin
        uart_statemachine_next <= uart_idle;
    end else begin
        case(uart_statemachine_next)
            uart_idle:
            begin
                uart_statemachine_next <= uart_start;
            end
            uart_start:
            begin
                uart_statemachine_next <= uart_transmit;
            end
            uart_transmit:
            begin
                // end of byte. Stop bit
                if(bit_position == 7) begin
                    uart_statemachine_next <= uart_stop;
                // stay until end of byte
                end else begin
                    uart_statemachine_next <= uart_transmit;
                end
            end
            uart_stop:
            begin
                uart_statemachine_next <= uart_start;
            end
        endcase
    end
end

Test bench:

`include "cross_com_sim.v"
`timescale 1ns/100ps

module cross_com_sim_tb;
    reg clk;
    reg rst;
    wire out_cross_com1;
    wire out_cross_com2;
    wire clk_out;
    cross_com_sim DUT(.clk(clk), .rst(rst), .out_cross_com1(out_cross_com1));

    initial
    begin
        $dumpfile("cross_com_sim_dump.vcd");
        $dumpvars;
    end

    initial
    begin
        clk = 1;
        forever #1 clk = ~clk;
    end

    initial
    begin
        rst = 1;
        #6;
        rst = 0;
        #4000;
        $finish;
    end
endmodule


Solution

  • I think the "next state" block should be written in combinational logic style so the state machine can run correctly. Like this:

    // Determine next state
    always @*
    begin
      case(uart_statemachine)
        uart_idle:  uart_statemachine_next = uart_start;
        uart_start: uart_statemachine_next = uart_transmit;
        uart_transmit: ...
        ...
      endcase
    end
    

    By the way, if reset signal comes from a source that is not synchronized with clock, like a push button on FPGA board. It should be synchronized with clock to prevent from meta- stable. Like this:

    // rst_in is the reset signal input
    // rst is the synchronized reset
    reg rst_meta;
    always @( posedge clk or posedge rst_in )
      if ( rst_in )
        { rst, rst_meta } <= 2'b11;
      else
        { rst, rst_meta } <= { rst_meta, 1'b0 };