Verilog Coding Conventions - jhu-cisst/mechatronics-firmware GitHub Wiki
Verilog Coding Conventions
The FPGA firmware is written in Verilog. Verilog is an expressive language, but we adopt a few conventions and design constructs that are documented on this page. Note that Verilog is used for applications other than FPGA synthesis (for example, simulation), so there are several legal Verilog constructs that are not supported for FPGA synthesis.
Coding Conventions
The following coding conventions should be used for all Verilog code written for this project. Exceptions should be clearly documented.
-
Multi-bit registers and wires are specified with the most significant bit first, e.g.,
wire[7:0]andreg[7:0]for 8-bit wires and registers. -
The least significant bit in multi-bit registers or wires should be 0.
-
Arrays are specified in ascending order, for example,
wire my_array[1:4]. -
Array can start with index 0 or 1. This is a consequence of the hardware design, which generally starts numbering at 1, and some subsequent design decisions in the firmware (see Numbering Convention below).
-
All registers (
reg) can be assumed to have initial values of 0, or can be set to a non-zero initial value by assignment (e.g.,reg my_reg = 1, orinitial my_reg = 1). Thus, it is not necessary to implement a reset signal to set the initial values.
Numbering Convention
One decision for any project is whether to start numbering at 0 or 1. Programming languages such as C/C++ will start at 0, but often humans start at 1. The FPGA firmware implementation is closely tied to the hardware, and the hardware design generally starts numbering at 1. For example, the four channels of the QLA are numbered 1-4. This also led to the design decision to use channel numbers that start at 1 when addressing hardware elements, saving channel 0 for special (board-level) use. See the Interface Specification for further details.
On the other hand, when creating a memory array (e.g., a lookup table) it is better to start at 0, so that all possible values of the index are covered. For example, a lookup table with a 4-bit index should be defined as wire my_table[0:15].
As a result, arrays in this project can be defined starting at 0 (e.g., when implementing a memory array) or at 1 (e.g., when interfacing with hardware).
Verilog Design Constructs
Verilog allows many different combinations of blocking (=) and non-blocking (<=) assignments, but the FPGA design in this project uses only the following three design patterns:
1. Combinatorial logic only
These are within a module scope, outside an always block. Example:
wire out;
wire in1;
wire in2;
assign out = in1 & in2; // output is AND of inputs
2. Sequential logic only (in always block)
These are within an always block, triggered by an input clock. Example:
wire new_value;
reg prev_value;
reg cur_value;
always @(posedge sysclk)
begin
prev_value <= cur_value;
cur_value <= new_value;
end
3. Combinatorial logic only (in always block)
This pattern is occasionally used to implement a multiplexer (mux), since it sometimes is more readable than using the ? : operators. Example:
wire select_input;
wire[3:0] mux_input0;
wire[3:0] mux_input1;
reg[3:0] mux_output;
always @(*)
begin
case (select_input)
0: mux_output = mux_input0;
1: mux_output = mux_input1;
endcase
end
Note that the above example is too simple, and would be more concise using combinatorial logic (Design Construct 1), where the mux would be specified as mux_output = select_input ? mux_input1 : mux_input0. In that implementation, mux_output would be a wire rather than a reg. Another design option would be to specify mux_input as an array,wire[3:0] mux_input[0:1], and then use subscripting, assign mux_output = mux_input[select_input].
Clock Domain Crossing
Clock domain crossing refers to the problem where the same signal is sampled (latched) by more than one clock. If these clocks are asynchronous, the signal setup and/or hold times can be violated, leading to metastability. The most common solution is to sample the signal with multiple flip-flops before using it in the new clock domain. In other cases, one can use dual-port RAM or a FIFO.
The original FPGA design (for FPGA V1) used the Firewire clock for (nearly) everything, thereby avoiding clock domain crossing problems. The Firewire clock, typically called sysclk, clk or clk1394, runs at 49.152 MHz, which has a period of 20.345 nsec.
Adding Ethernet in FPGA V2 did not cause any significant clock domain crossing problems because the KSZ8851 MAC/PHY provided a FIFO interface, and thus the Firewire clock continued to be the predominant clock in the FPGA design.
This changed with FPGA V3, which added two ports of Gigabit Ethernet, where a clock of 125 MHz is required. In addition, there appears to be clock domain crossing between the Zynq PS (microprocessor) and PL (FPGA). The Firewire clock is still used to run most of the functionality, but there is a significant amount of clock domain crossing for Ethernet and the EMIO interface to the processor.