Jump to content
  • 0

Problem with state machine, that sends 3 bytes data to UART tx


salamus

Question

Hi everyone! I am trying to write a fsm, that sends 3 bytes to uart transmitter, so that transmitter can send it to PC. I did this task with one byte, however 3 bytes pose a difficult task for me.

here is my fsm code:

// fsm has 4 states. When 'reset' is high, fsm should transfer from IDLE state to state byte1. After that,         
// check the 'busy' signal, that comes from uart tx. Busy is high when uart tx module is in the process of sending data/
// ready_2 signal is a signal, that tells uart tx to lock the incoming data

reset in my case is a signal from uart receiver, that asserts 1, when i send one byte from PC to to fpga via UART receiver. Basically, it is a signal, that tells us, that uart receiver finished receiving byte from pc.

============================================================
    always @(posedge clk)
    
    begin
            
            if (!reset) 
                begin
                    state     <= IDLE;
                    ready_2   <= 1'b0; // signal for UART transmitter to start sending data
                    data_store<= 1'b0; // register for 8-bit data
                end
                else 
                
                
                case (state)
                
                        IDLE: begin                     
                                    if (reset)
                                        state<=byte1;
                                    else
                                        state<=IDLE;
                                end
                        
                        byte1: begin
                                    ready_2<=1'b0;
                                    if (~busy) begin
                                        ready_2<=1'b1;
                                        data_store<=8'b00110101;
                                        state<=byte2;
                                        end
                                        else
                                        state<=byte1;
                                 end
                        byte2: begin
                                    ready_2<=1'b0;
                                    if (~busy) begin
                                        ready_2<=1'b1;
                                        data_store<=8'b10101010;
                                        state<=byte3;
                                        end
                                        else
                                        state<=byte2;
                                 end
                                
                        byte3: begin
                                    ready_2<=1'b0;
                                    if (~busy) begin
                                        ready_2<=1'b1;
                                        data_store<=8'b01110111;
                                        state<=IDLE;
                                        end
                                        else
                                        state<=byte3;
                                 end
                                
                                endcase
                                

                
            end

==============================================================

 

Code for UART tx is taken from here https://nandland.com/uart-serial-port-module/ and works just fine.

Can anyone, please, tell me, what am i doing wrong with fsm? I tried various methods, but the best i accomplished was to send one first byte

Link to comment
Share on other sites

5 answers to this question

Recommended Posts

  • 1

I think you need to wait one cycle between the busy signal going down and you setting ready_2 high.

I see in the source code of uart_tx that it reacts to i_Tx_DV (i.e., ready_2) going high only when uart_tx is in the state s_IDLE.

However, after setting o_Tx_Active low, uart_tx waits one cycle before going to state s_IDLE. This is to keep o_Tx_Done high exactly for one cycle. However, during this one cycle, you advance to the next state and set ready_2 to low. So, the uart_tx doesn't catch it.

I would introduce an intermediate state between the bytes, waiting for o_Tx_Done to go high.

Viktor

Link to comment
Share on other sites

  • 1

Hi @salamus

Could you share port, signal, and parameter declarations in addition to the state machine process? The data_store signal resetting to a one-bit value has some cause for concern, not necessarily for it being the cause of problems, but more in case there are other more relevant width mismatches - like if state is only one bit wide the state machine would presumably toggle back and forth between idle and byte1, depending on what the actual values of the state parameters are.

Also, if you haven't, simulate your design.

Thanks,

Arthur

Link to comment
Share on other sites

  • 1

Dunno exactly but it is very helpful to use a FIFO between the data generator and the UART transmitter. That way you can just write the output data ( one byte per clock cycle using AXI stream) to the FIFO and the communication between the FIFO and the UART takes care of loading bytes when the UART is ready.

Link to comment
Share on other sites

  • 1

Hey @salamus

On further review, Viktor is correct, an intermediate state that lets the state machine wait until the uart transmitter has acknowledged receipt of the byte (maybe by just waiting a cycle, since busy being low means you know it's ready to accept a byte) would help. The issue is likely that the state machine sees busy as being low, so asserts ready_2, and moves to the next state - this is all good, except that on the next clock cycle when it checks busy again, assuming busy is registered inside the uart transmitter, that busy signal hasn't had a chance to update yet and the state machine thinks it's okay to send another byte.

There's a comparable issue that requires that skid buffers be used in modules implementing AXI stream interfaces to be fully protocol compliant, since both sides of the handshake must be registered. There are some good articles around the web on this. This is an example: http://fpgacpu.ca/fpga/Pipeline_Skid_Buffer.html. It's not completely 1-to-1 with your issue, since there's a line in the AXI stream spec that states "a transmitter [your state machine] is not permitted to wait until TREADY [not busy] is asserted before asserting TVALID [ready_2]" (page 18, section 2.2: https://developer.arm.com/documentation/ihi0051/latest/), but is still a very similar registered handshake.

Edit: Richm is also correct, as a FIFO correctly implementing a handshake protocol would also take care of the issue. A skid buffer is just a very small FIFO.

Further edited: A third solution would be to eliminate the handshake entirely by holding ready continuously high as long as you aren't in reset and rewriting the state machine to either 1. select the byte to send combinationally depending on the state it's in or 2. set the data_store register to whatever byte you want to send in the next state (as in 8'b00110101 in idle, 8'b10101010 in byte1, etc).

Thanks,

Arthur

Link to comment
Share on other sites

  • 0

Thanks for the reply!

So,  declarations are:

/---------------------------------------------------------------/

    localparam IDLE      = 0;
    localparam byte1     = 1;
    localparam byte2    = 2;
    localparam byte3    = 3;

 reg [2:0] state;

reg [7:0] data_store;

wire ready2;

wire data_in;

assign ready2 = ready_2; //   ready2 is a wire connected to the input of uart_tx, a signal to start working with incoming byte

assign data_in = data_store;  // data_in is a wire connected to the data input of uart_tx.

//------ module uart_tx instantiation-------//

RS_232_OUT RS_232_OUT (
    .clk(clk), 
    .data(data_in),
     .busy(busy),
    .ready(ready2), 
    .data_out(DATA_OUT_232),
     .done(done)
    );

===================================

Also, certainly will try to follow this advice as soon as possible

15 hours ago, Viktor Nikolov said:

I think you need to wait one cycle between the busy signal going down and you setting ready_2 high.

I see in the source code of uart_tx that it reacts to i_Tx_DV (i.e., ready_2) going high only when uart_tx is in the state s_IDLE.

However, after setting o_Tx_Active low, uart_tx waits one cycle before going to state s_IDLE. This is to keep o_Tx_Done high exactly for one cycle. However, during this one cycle, you advance to the next state and set ready_2 to low. So, the uart_tx doesn't catch it.

I would introduce an intermediate state between the bytes, waiting for o_Tx_Done to go high.

Viktor

 

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...