Files
APS/Basic Verilog structures/Registers.md
Andrei Solodovnikov f3fcd27387 English version draft
Assisted-by: Claude:claude-4.6-sonnet
2026-05-07 16:04:46 +03:00

9.9 KiB

Describing Registers in SystemVerilog

Before describing memory, you need to learn how to describe individual registers. A register is a device for writing, storing, and reading n-bit binary data and performing other operations on it [1, p. 32]. In modern electronics, a register is most commonly built from D flip-flops. The ALU lab briefly mentioned that both wires and registers are declared using the logic type.

logic reg_name;

../.pic/Basic%20Verilog%20structures/registers/fig_01.drawio.svg

A register can have multiple inputs and one output. There are two essential inputs without which a register cannot exist: a data input and a clock input. In the figure, these are labeled D and clk. The optional reset input (rst) allows the register contents to be cleared regardless of the data input, and it can operate either synchronously with the clock signal (synchronous reset) or independently of it (asynchronous reset).

In addition, a register may have a write enable input (enable), which determines whether the data from the data input will be written to the register, and an optional set input (set), which forces the register value to logic one.

A register has a single output. In the figure above, it is labeled Q.

It is important to understand that the port names given here are not set in stone — they simply describe the functional purpose. When describing the behavior of a register, you will only operate on the register's name and the signals connected to it.

Since all signals in a digital circuit are carried over nets, it is convenient to think of a wire with the same name as the register as being implicitly connected to the register's output. This means you can use the register's name in subsequent digital logic:

../.pic/Basic%20Verilog%20structures/registers/fig_02.drawio.svg

So, we have placed a register on the schematic canvas — but how do we connect it to some logic? Suppose we have a clock signal and data we want to write:

../.pic/Basic%20Verilog%20structures/registers/fig_03.drawio.svg

This schematic corresponds to the following code:

module reg_example(
  input  logic clk,
  input  logic data,
  output logic reg_data
);

  logic reg_name;

endmodule

Clearly, we want to connect the clk signal to the register's clock input, data to the data input, and the register's output to the reg_data output:

../.pic/Basic%20Verilog%20structures/registers/fig_04.drawio.svg

Writing to a register is only possible on the edge of the clock signal. An edge is a transition of the signal from zero to one (rising edge) or from one to zero (falling edge).

The register description, along with the specification of the edge and clock signal, is done using the always_ff construct:

always_ff @(posedge clk)

Inside this construct, you specify what happens to the register contents. In our case, the data from the data input is written:

always_ff @(posedge clk) begin
  reg_name <= data;
end

Important

Note the <= operator. In this context, it is not the "less than or equal to" sign — it is the non-blocking assignment operator. There is also a blocking assignment operator (=), which can alter how the circuit is constructed for the same right-hand-side expression. While it may be considered poor educational practice, for now you simply need to remember: always use the non-blocking assignment operator <= when describing writes to a register. More details are provided in the document "On the Differences Between Blocking and Non-Blocking Assignments".

In addition, we need to connect the module's output to the register's output. This can be done using the continuous assignment operator assign, which you are already familiar with.

The complete code describing this schematic is as follows:

module reg_example(
  input  logic clk,
  input  logic data,
  output logic reg_data
);

  logic reg_name;

  always_ff @(posedge clk) begin
    reg_name <= data;
  end

  assign reg_data = reg_name;

endmodule

Suppose we want to add write control via enable and reset signals. This can be done as follows:

module reg_example(
  input  logic clk,
  input  logic data,
  input  logic reset,
  input  logic enable,
  output logic reg_data
);

  logic reg_name;

  always_ff @(posedge clk) begin
    if(reset) begin
      reg_name <= 1'b0;
    end
    else if(enable) begin
      reg_name <= data;
    end
  end

  assign reg_data = reg_name;

endmodule

Pay attention to the order of conditions. We first check the reset condition, and only then the write enable condition. If the write enable condition is checked first and the reset logic is placed in the else block, the register will not be reset when enable is asserted (the write operation takes priority over the reset). If the reset is described in a separate if block rather than in an else block, undefined behavior may occur: it becomes impossible to determine what will be written to the register if both reset and enable arrive simultaneously. Therefore, when a reset signal is present, all other write logic must be placed inside the else block.

Furthermore, EDA tools recognize the pattern used to describe a circuit element and, once recognized, implement it as the designer intended. For this reason, when describing a register, the reset signal (if used) must always be described first, followed by all other write logic in the else block.

The resulting schematic of a register with reset and write enable:

../.pic/Basic%20Verilog%20structures/registers/fig_05.drawio.svg

There is one more important rule to know when describing a register:

Assignment to a register may only occur within a single always block.

Even if the EDA tool does not immediately report an error, it will eventually appear during synthesis as a message related to "multiple drivers".

Combinational logic that precedes the register can also be described inside the register's assignment block. For example, the following schematic:

../.pic/Basic%20Verilog%20structures/registers/fig_06.drawio.svg

can be described as:

module reg_example(
  input  logic clk,
  input  logic A,
  input  logic B,
  input  logic reset,
  input  logic enable,
  output logic reg_data
);

  logic reg_name;

  always_ff @(posedge clk) begin
    if(reset) begin
      reg_name <= 1'b0;
    end
    else if(enable) begin
      reg_name <= A & B;
    end
  end

  assign reg_data = reg_name;

endmodule

However, this is merely a shorthand. If you know how to describe a register with a single wire connected to its data input, you can still describe this schematic:

module reg_example(
  input  logic clk,
  input  logic A,
  input  logic B,
  input  logic reset,
  input  logic enable,
  output logic reg_data
);

  logic reg_name;     // Note that even though both
  logic ab;           // reg_name and ab are declared as logic,
                      // ab will become a wire and reg_name a register
                      // (due to the continuous assignment for ab and the
                      // always_ff block for reg_name)

  assign ab = A & B;

  always_ff @(posedge clk) begin
    if(reset) begin
      reg_name <= 1'b0;
    end
    else if(enable) begin
      reg_name <= ab;
    end
  end

  assign reg_data = reg_name;

endmodule

This is why it is so important to understand the fundamental way of describing a register.

Moreover, from the synthesizer's perspective, this description is easier to synthesize because it does not need to separate the combinational and synchronous parts from within a single always block.

In general, a register is a multi-bit construct (in the earlier example, a 1-bit register could be represented as a simple D flip-flop). Creating a multi-bit register differs little from creating a multi-bit wire, and the write logic for a multi-bit register is identical to that of a 1-bit register:

module reg_example(
  input  logic        clk,
  input  logic [7:0]  data,
  output logic [7:0]  reg_data
);

  logic [7:0] reg_name;

  always_ff @(posedge clk) begin
    reg_name <= data;
  end

  assign reg_data = reg_name;

endmodule

Chapter Summary

  1. A register is a basic storage element that retains its state as long as the circuit is powered.
  2. A register is declared using the logic type; if needed, the bit width is specified after the type.
  3. The write logic for a register is described using the always_ff block, whose parentheses specify the clock signal and the edge on which writing occurs, as well as the reset signal (in the case of an asynchronous reset).
  4. A register can have various control signals: set, reset, and write enable. The logic for these control signals is part of the register's write logic and is also described inside the always_ff block.
  5. When describing register write logic, the non-blocking assignment operator <= must be used.
  6. Write logic for a register must not be described in more than one always block (in other words, the assignment operation for each register may only appear in a single always block).

Test Yourself

How would you describe the schematic shown below in SystemVerilog?

../.pic/Basic%20Verilog%20structures/registers/fig_07.drawio.svg

References

  1. Sh. Gabrielyan, E. Vakhtina / Electrical Engineering and Electronics. Methodological Guidelines. — Stavropol: Argus, 2013