Take the 2-minute tour ×
Electrical Engineering Stack Exchange is a question and answer site for electronics and electrical engineering professionals, students, and enthusiasts. It's 100% free, no registration required.

I have coded my own implementation of a serial receiver. It will work for incoming data at a baud rate of 115200.

Here's my code:

module my_serial_receiver(
    input clk,
    input reset_n,
    input Rx,
    output reg [7:0] received_byte,
    output reg byte_ready
);

parameter IDLE = 4'd0, BIT_0 = 4'd1, BIT_1 = 4'd2,
                BIT_2 = 4'd3, BIT_3 = 4'd4, BIT_4 = 4'd5, BIT_5 = 4'd6, 
                BIT_6 = 4'd7, BIT_7 = 4'd8, BYTE_READY = 4'd9;

reg [3:0] state = 0;
reg [7:0] baud_rate_clock = 0;

always @(posedge clk) begin
  if (baud_rate_clock == 8'd216) begin
    baud_rate_clock <= 0;
  end
  else begin
    baud_rate_clock <= baud_rate_clock + 8'd1;
  end
end

assign baud_tick = ~|baud_rate_clock;

always @(posedge baud_tick or negedge reset_n) begin
    if (~reset_n) begin
        state <= 0;
        byte_ready <= 0;
    end
    case(state)
        IDLE: begin
             byte_ready <= 0;
             if (Rx == 0) state <= BIT_0;
        end
        BIT_0: begin
           received_byte[0] <= Rx;
           state <= BIT_1;
        end
        BIT_1: begin
             received_byte[1] <= Rx;
             state <= BIT_2;
        end
        BIT_2: begin
             received_byte[2] <= Rx;
             state <= BIT_3;
        end
        BIT_3: begin
             received_byte[3] <= Rx;
             state <= BIT_4;
        end
        BIT_4: begin
             received_byte[4] <= Rx;
             state <= BIT_5;
        end
        BIT_5: begin
             received_byte[5] <= Rx;
             state <= BIT_6;
        end
        BIT_6: begin
             received_byte[6] <= Rx;
             state <= BIT_7;
        end
        BIT_7: begin
             received_byte[7] <= Rx;
             state <= BYTE_READY;
        end
        BYTE_READY: begin
             byte_ready <= 1;
             state <= IDLE;
        end
        default: state <= IDLE;
    endcase
end      


endmodule

Here are my simulation results:

Simulation results

So basically, the simulation is reacting exactly how I want it to.

I sent 0x55, 0x74, and 0x40, and that is when byte_ready is asserted, so I know that's correct.

Furthermore, my baud_tick signal is properly asserted 115200 times a second.

Over all, my simulation appears to be perfect.

When I actually program the FPGA and connect the Rx line, nothing happens (as in, no LEDs are flashing). I have coded it such that the 8 LEDs take on the values of the 8 bits of the byte. I have tested this method before with someone else's implementation and it worked perfectly for testing the module.

I have even used the SignalTap logic analyzer and confirmed the incoming Rx data is correct, and the baud rate is correct with + or - 0.5% error.

I have no idea what to do from here. Any suggestions?

share|improve this question
1  
It would help if you posted at least a part of the Rx code. You say signaltap shows the Rx data is correct but "nothing" happens. What does that mean? What do the LEDs indicate, the values of the bytes as they're received or a running count of the number of bytes received? –  Ciano Jun 5 at 0:34
    
@Ciano Thanks, I edited the question. I realized the code is put in a scroll field so that's good. –  thejohnny Jun 5 at 0:50
    
If the top module instantiates it, it might help if you posted it. BTW, the code has a problem, which is that it does not sample the bits in the middle, but this is unrelated to the LEDS not flashing at all. –  apalopohapa Jun 5 at 2:07
add comment

2 Answers 2

FPGA code works in behavioral simulation but not in hardware

This happens sometimes. So maybe something is wrong with the hardware, and maybe the simulation doesn't accurately model what happens in reality. Have to check both.

I assume you're a student, since there is free ready-made code that already implements serial UARTs, so my answer is about how to approach debugging this kind of problem.

1. Did synthesis really succeed?

When you build the project, are there any diagnostic or warning messages? Does the synthesized code make sense?

Examine the post-translation/post-synthesis code that is generated by the various synthesis tools.

As written, the code you posted is behavioral (always @posedge), not structural (instances and wires). That makes it very straightforward to simulate, but the synthesis tool has to try to recognize what you're asking for by matching against templates. If your always statement doesn't match one of the templates, then the synthesis tool won't know how to implement it. That should generate a diagnostic message somewhere.

The first translation phase generates RTL code (Register Transfer Logic) which is generic combinational logic plus flip-flops. In this example, you should expect to see some flip-flops for the baud rate counter and the state machine, and some combinational logic to determine next state. Later synthesis phases figure out how to configure the FPGA to implement that RTL code. These mapping and place-and-route phases choose which slice (Xilinx) or configurable logic block (Altera) will be which flip-flop, which pin to use for which toplevel input/output ports, and how to connect it all together.

After translation/mapping/place-and-route, you can go back and run post-mapping or post-place-and-route simulation. This simulates the RTL code that the synthesis tool generated, not the behavioral code you started with. This simulation knows where each flip-flop is located and how many picoseconds of delay each route has, so you can see a more realistic simulation of the timing edges. (Since your clock is only 25MHz I expect there should be plenty of timing margin.)

Simulation and synthesis are very different. An always block that works fine in simulation may unexpectedly be omitted after synthesis. (I once lost half of a fairly complex system because of an "incomplete initialization ignored" warning in a low-level module, which caused Xilinx ISE 14.7 to replace one entire module with a constant 0. And all of the higher-level modules that depended on that signal, got optimized away to nothing too, since their outputs became constant. Since there was no longer any internal logic to drive my SPI output, the output pin didn't get routed. Now I've developed the habit of checking the RTL after synthesis to make sure there's no missing pieces.)

I come from a C programming background, so I constantly have to challenge myself when writing Verilog: what hardware do I want it to synthesise, then make sure I'm describing it in a way that the synthesis tool can understand.

2. What is the hardware actually doing?

Since the FPGA is doing nothing that you can directly observe, it's hard to diagnose whether the FPGA is powered up, configured with your code, and running your state machine. Modify your toplevel to bring out some diagnositc outputs:

  • Bring state[3:0] to four of the LEDs

  • Bring baud_tick signal to an LED

  • Add a "heartbeat" LED blinker to your toplevel (just toggle a D flip-flop at half the clk rate), to help confirm that the FPGA firmware is configured. This diagnositc is worth giving up one of the LEDs, at least for now.

  • Bring received_byte[0] (the first and least significant bit) to an LED

  • Bring received_byte[7] (the last and most significant bit) to an LED

This does mean using the eight LEDs on your board for these diagnostic signals, instead of the received_byte[7:0] parallel data out. But these lower-level signals give you more useful information to see what's going in inside your module. You can change it back after you get the bugs fixed.

I assume your FPGA board has some debounced slide switches. Now's the time to put them to work:

  • Use a debounced slide switch to drive reset

  • Use a debounced slide switch to drive Rx

  • Use a debounced slide switch to drive clk

Temporarily change your baud rate parameter so you get baud_tick every 7 clock cycles, so that you can manually clk-toggle through all the states without losing track of time.

Step through the state machine slowly, on real hardware.

  • Check that the heartbeat LED is blinking (alternating 1/2 clk frequency)

  • Check that baud_tick gives one pulse at the expected rate (HINT: I think this is where you'll find the problem. What if initial baud_rate_clock value is random?)

  • Check that the state machine goes to the correct state out of reset.

  • Check that your state machine stays in the idle state when Rx is idle.

  • Check that your state machine detects the start bit.

  • Check that your state machine cycles through the states correctly and returns to idle state.

  • Check that the received_byte[0] and received_byte[7] values are what you expect.

An oscilloscope is a real lifeline if you have one available, this would let you see the hardware running at full speed without needing to toggle through with debounced switches. For a more complicated design this would be an essential tool.

3. Why doesn't the simulation match reality?

Fully testing a design is a really tough problem. Especially when it's your own code. In this example, your code has inputs clk, reset, and Rx. You're already driving clk.

  • What happens if Rx is in the "wrong" state when exiting reset? Will the state machine recover?

  • What if the transmitter is using a different baud rate (+5%, -5%, +10%, -10%, x2, x0.5)

  • What if the state machine starts up in an random state, will it recover or will it get stuck?

  • What if the Rx frame has the wrong stop bit value?

  • What if the Rx is mark when you're expecting space?

  • What if the Rx is inverted? (This is a very common error in RS232 UART systems, because the logic level to RS232 level translator typically inverts.)

You get the idea. Coming up with all the test conditions is as hard as any other kind of design work. There will always be coverage gaps, so plan on going back and adding more tests based on what you find in checking the hardware.

One of the hardest things in both programming and in HDL design, is testing your own designs. When I write a test bench for my own code, I always worry that I haven't covered enough possible test cases. And any test case that I can think of, I've probably dealt with it in my code -- it's the tests that I didn't think of that cause gaps in test coverage. For example, what if the input signal is not synchronized with the device under test? That's very common in real hardware, but can be tricky to set up in a behavioral simulation.

Hope this is enough to get you on your way. Good luck!

share|improve this answer
    
+1 for thoroughly reading the synthesis logs, most likely the reason is there. –  shuckc Jun 5 at 8:57
    
@MarkU Thank you so much for such a detailed write up. I will definitely be reffering to this post a lot during my FPGA work. I attempted you debugging technique with the LEDs and it showed that only baud_tick and the clk LED were lit. Apparently nothing else is happening. After giving it some thought, I have realized that I designed my UART in a synchronous fashion which is quite opposite whole point of a Universal Asynchronous receiver/transmitter. I will try to redesign it in an asynchronous manner and see what happens. –  thejohnny Jun 5 at 20:44
add comment

Your code is not synthesizable because of a there needs to be an else before case. You would have caught it in simulation by testing the reset_n pin.

  • Required changes are in bold
  • Recommend enhancements are bold-italic
always @(posedge baud_tick or negedge reset_n) begin
    if (~reset_n) begin
        state <= IDLE; // not a bug, but state should reset to IDLE, not 0
        byte_ready <= 1'b0; // not a bug, but size and radix should be defined
        // it is recommend not to place a non-async reset in a block with async reset
        received_byte <= 8'h0; // reset signal or move to another always block
    end
    else begin // <== synthesis bug here, the "else begin" was missing <==
      case(state)
        // ... your state code ...
      endcase
    end// end to match begin 
end

This missing else error should be in your synthesis log/report.

share|improve this answer
    
I added your change but it had no effect. –  thejohnny Jun 5 at 20:41
    
@thejohnny, anything in the synthesis log? –  Greg Jun 5 at 22:28
    
sorry im quite new to quartus. Where can i find that log? –  thejohnny Jun 5 at 22:34
    
@thejohnny, I don't have access to Quartus. You'll need to check the user manual and tutorials. –  Greg Jun 6 at 16:21
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.