{josuah.net} | {panoramix-labs.fr} (DIR) • {josuah.net} (DIR) • {panoramix-labs.fr} {git} | {cv} | {links} | {quotes} | {ascii} | {tgtimes} | {gopher} | {mail} (DIR) • {git} (BIN) • {cv} (DIR) • {links} (DIR) • {quotes} (DIR) • {ascii} (HTM) • {tgtimes} (DIR) • {gopher} ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Sequential signals may hide combinational ones ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Verilog is a hardware language with a syntax similar to C, but very different semantics: signals, circuits, and registers for storing the state. Registers and sequential logic ────────────────────────────── ┊ ┌─────┐ ┊ some circuit >──┤D Q├──> some circuit ┊ │ │ updated one clock edge later ┊ ┌─> │ ┊ │ └─────┘ ┊ clock They use the `<=` assignment operator to build-up sequential logic. Like software variables, they can hold values across multiple clock cycles. An assignment to a register (on `D`) gets read (on `Q`) on the next clock cycle. ┊ reg r; ┊ always_ff @(posedge clk) begin ┊ r <= 1; └── This is the clock connected to all registers ┊ end └── This is the input signal D fed into the register ┊ ┊ wire w = r; ┊ └── This is the output signal Q read from the register Wires, signals, and combinational logic ─────────────────────────────────────── ┊ some circuit >───────────────> some circuit ┊ the_wire_name Most verilog expressions gets converted to a circuit of gates implementing it. The `=` assignment used in various contexts permit to attach an end of a wire to a name, a label. The `always_comb` block can only have `=`, no `<=`, and implement combinational logic. ┊ input i; ┊ wire [7:0] w; ┊ always_comb begin ┊ w = i ^ 8'b10101010; ┊ end How combinational logic appear ────────────────────────────── But what if complex expressions are assigned to a register instead of a wire? The signal coming into the register may be represented as an expression itself, and expressions are combinational logic. The flip-flop-only `always_ff` contains, in fact, a mix of combinational and sequential logic. The restriction in `always_ff` is that adding extra combinational logic is forbidden (no `=` operator) but combinational logic feeding registers can still occur. It helps with preventing `<=` being typoed into `=`. ┊ input i; ┊ reg [7:0] r; ┊ always_ff @(posedge clk) begin ┊ r <= i ^ 8'b10101010; ┊ end This is not a problem per-se, just a remark. The LowRISC approach ──────────────────── (HTM) The above permits us to explain the approach {LowRISC} is taking. The team (HTM) described it in their {style guide for SystemVerilog}: It is done by coupling a `always_ff` that applies the changes to registers, with `always_comb` that build-up the next values. Each register is having: • a `_d` wire that is fed into the register, driving the next value of the register; • a `_q` register, representing the output value of the register, which is delayed by one clock. On each clock, this happens alone in a block: `something_q <= something_d`; and the rest is purely combinational. In other words, the sequential operation have been isolated, by giving the input signal a name (`something_d` here). ┊ wire something_d; ┊ reg [7:0] something_q; ┊ always_ff @(posedge clk) begin ┊ // apply the changes to the registers ┊ something_q <= something_d; ┊ end ┊ ┊ always_comb begin ┊ // by default, the value stays the same as the register previous value ┊ // this was happening under the hood in the previous examples, it is now ┊ // explicit ┊ something_d = someting_q; ┊ ┊ // for giving something_q another value, something_d can be changed with ┊ // in the combinational logic ┊ if (i > 3) begin ┊ something_d = something_d + 1; ┊ end ┊ end Extra convention ──────────────── In addition to LowRISC guideline, I also use this convention: When declaring signals with `logic`, the `_d` wire comes first, followed by `_q` and `_q2` etc. That way, there is one line per signal, including its past state. This help with reading code faster: ┊ logic ack_d, ack_q, ack_q2; ┊ logic state_d, state_q; ┊ logic counter_q; In practice? ──────────── Inconvenients: • More verbose outside the combinational block; • Extra signals to declare (the `_d` ones): Advantages: • Everything is explicit around the registers; • In expressions using these signals, change `_q` into `_d` to save one clock cycle; • Consistent naming using the `Q`/`D` signal naming of registers. It is also frequent to have a reset signal that puts all registers to a default value. The LowRISC approach makes it convenient to integrate it within the `always_ff` block, introducing a little bit of combinational logic, without much effect. ┊ always_ff @(posedge clk) begin ┊ if (rst) begin ┊ something0_q <= 0; ┊ something1_q <= 0; ┊ something2_q <= 0; ┊ something3_q <= 0; ┊ end else begin ┊ something0_q <= something0_d; ┊ something1_q <= something1_d; ┊ something2_q <= something2_d; ┊ something3_q <= something3_d; ┊ end ┊ end In a real world example, I would have been using `logic` instead of `wire` and `reg`, the `_q` and `_d` helping with making the distinction. I would also likely use `rst_ni` instead of `rst` and `clk_i` instead of `clk`. Also sometimes the whole design is so trivial that a separate `always_comb` would be an overkill. Links ───── (HTM) {https://lowrisc.org/} (HTM) {https://github.com/lowRISC/style-guides/blob/master/VerilogCodingStyle.md}