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. But the oscilloscope screenshot shows, that two cycles of stop-bits are being output. 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
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 };