eee:fpga:led_blink

这是本文档旧的修订版!


LED Blink

Create an empty project like led_toggle did.

Sequential RTL usually describes behavior in terms of clock edges rather than absolute time. always @(posedge clk) reacts to each rising edge regardless of whether the clock is 50 MHz or 100 MHz. However, the physical circuit does care about the clock period, because the combinational logic between registers must settle before the next sampling edge.

For example,

  1. always @(posedge clk) begin
  2. counter1 <= counter1 + 1;
  3. counter2 <= counter2 + 1;
  4. end

This block is asking the FPGA to increase both counter1 and counter2 signals by 1 when at clk signal posedge (raising from 0 to 1). Take note that the change of both signals happen at the same time independently, as they are parallel in physical space. It will not like programming language to increase counter1 first then increase counter2.

Here we use (non-blocking assignment) instead of = (blocking assignment), and they have different Verilog simulation semantics, which affects how a hardware description behaves and can cause simulation results to differ from the intended synchronous hardware behavior. In simulator, blocking assignment behaves like the programming language, the first line complete first (and blocking the next line to execute), then is the second line. While the the non-blocking assignment get ready all new values first, and then update all together. Refer to page assignment for more details.

For beginner, it is good to keep all sequential logic with non-blocking assignment , and combinational logic in blocking assignment =.

The ypcb-00338 board have a crystal of 50 MHz (and also another 2 in 200 MHz). In order to toggle LED every 1 second, or 2 Hz, we need to count from 0 to 49,999,999, and then toggle led and reset counter.

  1. if (counter >= 26'd49_999_999) begin
  2. counter <= 0;
  3. led <= ~led;
  4. end
  5. else begin
  6. counter <= counter + 1;
  7. end

We need the counter signal that is at least able to store 49,999,999. Since

$$ 2^{25} = 33,554,432 < 50,000,000 \\ 2^{26} = 67,108,864 > 50,000,000 $$

26-bit is enough. Thus the definition of counter should be

  1. reg [25:0] counter;

where the MSB is bit 25 and LSB is bit 0.

Before we combining everything together, we need to give initial values to our regs counter and led_state, so that they are not starting from any random values that we are not expecting.

top.v
  1. initial begin
  2. counter = 0;
  3. led_state = 0;
  4. end

For this Xilinx 7-series FPGA using Vivado, synthesizable initial assignments can specify the configuration-time initial values of registers. The FPGA does not execute the initial block like software; Vivado maps the requested initial state into the device configuration.

The top module will be

top.v
  1. module top (
  2. input wire clk,
  3. output wire led
  4. );
  5.  
  6. reg [25:0] counter;
  7. reg led_state;
  8.  
  9. initial begin
  10. counter = 0;
  11. led_state = 0;
  12. end
  13.  
  14. always @(posedge clk) begin
  15. if (counter >= 26'd49_999_999) begin
  16. counter <= 0;
  17. led_state <= ~led_state;
  18. end
  19. else begin
  20. counter <= counter + 1;
  21. end
  22. end
  23.  
  24. assign led = led_state;
  25.  
  26. endmodule

Similarly, we need to attach ports to physical pins.

top.xdc
  1. set_property PACKAGE_PIN AA28 [get_ports clk]
  2. set_property IOSTANDARD LVCMOS18 [get_ports clk]
  3.  
  4. set_property PACKAGE_PIN P30 [get_ports led]
  5. set_property IOSTANDARD LVCMOS18 [get_ports led]
  6.  
  7. create_clock -period 20.000 [get_ports clk]

One additional line create_clock -period 20.000 [get_ports clk] is telling Vivado that the clk signal have a period of 20 ns, to asking analyzer that the logic must be completed within this period.

It is expected that the red LED in sequence that lights up one second and blows down one second.

Open left side menu - analysis - open elaborated design - schematic, you will see the electronic diagram from the Verilog code.

The counter0_i adds up counter0_i.I0[25:0], which is the current value of register counter_reg[25:0] (counter_reg[25:0].Q), and counter0_i.I1, which is connected to high level directly. The value is temporarily kept at counter0_i.O[25:0], or counter_reg[25:0].D. When the clk at posedge, counter_reg[25:0] will be updated from counter_reg[25:0].D. This is how the sentence

  1. counter <= counter + 1;

works.

The counter0_i__0 compares counter0_i__0.I0[25:0] (counter_reg[25:0].Q) and a fixed value at counter0_i__0.I1[25:0], 0x2FAF07F, or 49,999,999. The output counter0_i__0.O goes to two places, one is counter_reg[25:0].RST, which will reset the value, and the other is led_state_reg.CE. This corresponds to the block

  1. if (counter >= 26'd49_999_999) begin
  2. counter <= 0;
  3. led_state ...
  4. end

The led_state_reg is another register similar to couter_reg[25:0]. It will only take action when led_state_reg.CE (clock enable) is high. When led_state_reg.CE is high and led_state_reg.C is at posedge, it will update its value from led_state_reg.D, which is operated from led_state_reg.Q using an inverter led_state0_i. This correcponds the phrase

  1. led_state <= ~led_state;

When led_state_reg.CE is low, led_state_reg.Q remains the same regardless of led_state_reg.C, and nothing will be changed.

From left side menu - synthesis - open synthesized design - schematic, how the FPGA actually plan the resources will be shown.

The input clk signal will go through input buffer IBUF and global clock buffer BUFG before it can be used. And the output signal led must be processed via output buffer OBUF. The RTL_REG_SYNC counter_reg[25:0] and RTL_REG led_state_reg from RTL schematic is expanded to multiple FDRE (D-type Flip-Flop primitive), the RTL_ADD counter0_i is also replaced with multiple CARRY4 dedicated fast carry-chain primitive used to implement arithmetic and comparison logic. RTL_GEQ counter0_i__0 and RTL_INV led_state0_i are formed using multiple LUT (look-up table) unit, which stores the truth table of different blocks.

Verilog is not a sequence of instructions executed by the FPGA. Clocked registers store the current state. Combinational logic continuously computes values from the current signals. At a clock edge, registers sample their inputs and establish the next state. Vivado progressively transforms:

RTL description → elaborated logic → FPGA primitives → placed and routed hardware → bitstream

  • eee/fpga/led_blink.1786798528.txt.gz
  • 最后更改: 2026/08/15 12:55
  • xiaobenmao