Bringing up a custom FPGA board or soft-core microcontroller block requires structured verification: starting from clock distribution constraints, to pin mappings, simulation setups, and finally, executing test firmware. This post serves as a technical log detailing clock divider VHDL blocks, Verilog interface modules, assembly test routines, and ModelSim compile scripts.
VHDL Clock Divider Module
For clock routing and sub-harmonic clock generation, we use a simple VHDL clock divider. VHDL is strongly-typed and highly structured, making it suitable for safe clock-tree definitions.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity clock_divider is
generic (
DIV_FACTOR : integer := 4
);
port (
clk_in : in std_logic;
reset_n : in std_logic;
clk_out : out std_logic
);
end entity clock_divider;
architecture rtl of clock_divider is
signal r_counter : integer range 0 to (DIV_FACTOR/2)-1 := 0;
signal r_clk_reg : std_logic := '0';
begin
process(clk_in, reset_n)
begin
if reset_n = '0' then
r_counter <= 0;
r_clk_reg <= '0';
elsif rising_edge(clk_in) then
if r_counter = (DIV_FACTOR/2)-1 then
r_counter <= 0;
r_clk_reg <= not r_clk_reg;
else
r_counter <= r_counter + 1;
end if;
end if;
end process;
clk_out <= r_clk_reg;
end architecture rtl;
Verilog Reset Synchronizer
Metastability is a significant hazard during FPGA power-up. We implement a classic dual-flop reset synchronizer in Verilog to align external resets to the local clock domain.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
module reset_synchronizer (
input wire clk,
input wire rst_n_in,
output wire rst_n_out
);
reg sync_reg_0;
reg sync_reg_1;
always @(posedge clk or negedge rst_n_in) begin
if (!rst_n_in) begin
sync_reg_0 <= 1'b0;
sync_reg_1 <= 1'b0;
end else begin
sync_reg_0 <= 1'b1;
sync_reg_1 <= sync_reg_0;
end
end
assign rst_n_out = sync_reg_1;
endmodule
RISC-V Assembly Boot Test
For testing the soft-core processor embedded in the logic array, we run a short RISC-V assembly script that initializes registers, clears memory sections, and blinks a status LED.
.section .text
.global _start
_start:
# Initialize stack pointer
la sp, _stack_pointer
# Zero-out the BSS segment
la a0, _bss_start
la a1, _bss_end
zero_loop:
bgeu a0, a1, boot_fw
sw zero, 0(a0)
addi a0, a0, 4
j zero_loop
boot_fw:
# Blink LED routine
li t0, 0x40001000 # Address of GPIO Output register
li t1, 0x01 # Output value (LED on)
blink_loop:
sw t1, 0(t0) # Write to GPIO
li t2, 500000 # Loop count delay
delay_loop:
addi t2, t2, -1
bnez t2, delay_loop
xori t1, t1, 0x01 # Toggle LED bit
j blink_loop
ModelSim Testbench Simulation Commands
To verify our VHDL and Verilog designs in ModelSim before board deployment, we write a compile and simulation script in Tcl:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Create project library
vlib work
vmap work work
# Compile VHDL source files
vcom -93 -work work ./src/clock_divider.vhd
# Compile Verilog source files
vlog -work work ./src/reset_synchronizer.v
# Compile Testbench
vlog -work work ./sim/tb_system.v
# Start simulator with 1ps precision (disable optimization for debug)
vsim -t 1ps -novopt work.tb_system
# Add waves to viewer window
add wave -position insertpoint sim:/tb_system/*
add wave -position insertpoint sim:/tb_system/dut/clock_divider/*
# Run simulation for 10 microseconds
run 10 us