refactor & add tbs

This commit is contained in:
Kostas Drakontidis
2026-11-04 12:50:44 +02:00
parent 59f2c10c51
commit 3863bfbecd
11 changed files with 514 additions and 247 deletions
@@ -0,0 +1,19 @@
## This file is a general .xdc for the Spartan-7 Boolean Board
### Clock Signal
set_property -dict { PACKAGE_PIN F14 IOSTANDARD LVCMOS33 } [get_ports { clk }];
create_clock -add -name sys_clk -period 10.00 -waveform {0 5} [get_ports { clk }];
### 7-Segment Display
set_property -dict { PACKAGE_PIN D7 IOSTANDARD LVCMOS33 } [get_ports { a }];
set_property -dict { PACKAGE_PIN C5 IOSTANDARD LVCMOS33 } [get_ports { b }];
set_property -dict { PACKAGE_PIN A5 IOSTANDARD LVCMOS33 } [get_ports { c }];
set_property -dict { PACKAGE_PIN B7 IOSTANDARD LVCMOS33 } [get_ports { d }];
set_property -dict { PACKAGE_PIN A7 IOSTANDARD LVCMOS33 } [get_ports { e }];
set_property -dict { PACKAGE_PIN D6 IOSTANDARD LVCMOS33 } [get_ports { f }];
set_property -dict { PACKAGE_PIN B5 IOSTANDARD LVCMOS33 } [get_ports { g }];
set_property -dict { PACKAGE_PIN A6 IOSTANDARD LVCMOS33 } [get_ports { dp }];
set_property -dict { PACKAGE_PIN D5 IOSTANDARD LVCMOS33 } [get_ports { an0 }];
set_property -dict { PACKAGE_PIN C4 IOSTANDARD LVCMOS33 } [get_ports { an1 }];
set_property -dict { PACKAGE_PIN C7 IOSTANDARD LVCMOS33 } [get_ports { an2 }];
set_property -dict { PACKAGE_PIN A8 IOSTANDARD LVCMOS33 } [get_ports { an3 }];
### Button(s)
set_property -dict { PACKAGE_PIN J2 IOSTANDARD LVCMOS33 } [get_ports { reset }];
+108
View File
@@ -0,0 +1,108 @@
`timescale 1ns / 1ps
module tb_driver;
reg clk = 0;
reg reset = 1;
wire an3, an2, an1, an0;
wire a, b, c, d, e, f, g, dp;
FourDigitLEDdriver dut (
.clk(clk),
.reset(reset),
.an3(an3),
.an2(an2),
.an1(an1),
.an0(an0),
.a(a),
.b(b),
.c(c),
.d(d),
.e(e),
.f(f),
.g(g),
.dp(dp)
);
always #5 clk = ~clk; // 100 MHz
wire [3:0] an = {an3, an2, an1, an0}; // active-low
wire [3:0] lit = ~an; // 1 = anode enabled
wire [6:0] seg = {a, b, c, d, e, f, g}; // active-low
integer errors = 0;
integer scans = 0;
// 1) No double anodes lit at the same time
always @(an)
if ((lit & (lit - 4'd1)) != 4'd0) begin
errors = errors + 1;
$display("ERROR t=%0t: overlapping anodes an=%b", $time, an);
end
// 2) No segment change on lit anode
realtime t_seg_change = 0;
always @(seg) begin
t_seg_change = $realtime;
if (lit != 4'd0) begin
errors = errors + 1;
$display("ERROR t=%0t: segment change on lit anode", $time);
end
end
// 3) Margin: stable data >= 200 ns before anode off
reg [3:0] prev_lit = 4'd0;
always @(lit) begin
if (prev_lit == 4'd0 && lit != 4'd0 && ($realtime - t_seg_change) < 200.0) begin
errors = errors + 1;
$display("ERROR t=%0t: not enough margin", $time);
end
prev_lit = lit;
end
// 4) Correct digit for each anode (0123, left to right)
task check_seg(input [6:0] expected);
begin
#100;
if (seg !== expected) begin
errors = errors + 1;
$display("ERROR t=%0t: an=%b seg=%b expected=%b", $time, an, seg, expected);
end
end
endtask
always @(an) begin
case (an)
4'b0111: check_seg(7'b0000001); // AN3: 0
4'b1011: check_seg(7'b1001111); // AN2: 1
4'b1101: check_seg(7'b0010010); // AN1: 2
4'b1110: check_seg(7'b0000110); // AN0: 3
default: ;
endcase
end
// 5) Scan period = 16 x 200 ns = 3200 ns
realtime t_an3_fall = 0;
always @(negedge an3) begin
scans = scans + 1;
if (t_an3_fall != 0 &&
(($realtime - t_an3_fall) < 3199.0 || ($realtime - t_an3_fall) > 3201.0)) begin
errors = errors + 1;
$display("ERROR t=%0t: scan period %0f ns", $time, $realtime - t_an3_fall);
end
t_an3_fall = $realtime;
end
initial begin
#100 reset = 0;
#100000; // 100 us (includes MMCM lock)
if (scans < 10) begin
errors = errors + 1;
$display("ERROR: %0d scans (MMCM not locked)", scans);
end
if (errors == 0) $display("PASS: %0d scans no errors", scans);
else $display("FAIL: %0d errors", errors);
$finish;
end
endmodule
@@ -0,0 +1,24 @@
module LEDdecoder (
input [3:0] char,
output [6:0] LED
);
assign LED = (char == 4'h0) ? 7'b1111110 : // 0
(char == 4'h1) ? 7'b0110000 : // 1
(char == 4'h2) ? 7'b1101101 : // 2
(char == 4'h3) ? 7'b1111001 : // 3
(char == 4'h4) ? 7'b0110011 : // 4
(char == 4'h5) ? 7'b1011011 : // 5
(char == 4'h6) ? 7'b1011111 : // 6
(char == 4'h7) ? 7'b1110000 : // 7
(char == 4'h8) ? 7'b1111111 : // 8
(char == 4'h9) ? 7'b1111011 : // 9
(char == 4'hA) ? 7'b1110111 : // A
(char == 4'hB) ? 7'b0011111 : // B
(char == 4'hC) ? 7'b1001110 : // C
(char == 4'hD) ? 7'b0111101 : // D
(char == 4'hE) ? 7'b1001111 : // E
(char == 4'hF) ? 7'b1000111 : // F
7'b0000000;
endmodule
@@ -0,0 +1,91 @@
module FourDigitLEDdriver (
input clk,
input reset,
output an3,
an2,
an1,
an0,
output a,
b,
c,
d,
e,
f,
g,
dp
);
wire clk_fb_out, clk_fb, clk_slow_raw, clk_slow, locked;
MMCME2_BASE #(
.BANDWIDTH ("OPTIMIZED"),
.CLKIN1_PERIOD (10.0), // 100 MHz
.DIVCLK_DIVIDE (1),
.CLKFBOUT_MULT_F(6.0), // VCO = 600 MHz
.CLKOUT1_DIVIDE (120), // 600/120 = 5 MHz
.STARTUP_WAIT ("FALSE")
) MMCME2_BASE_inst (
.CLKIN1 (clk),
.CLKFBOUT(clk_fb_out),
.CLKFBIN (clk_fb),
.CLKOUT1 (clk_slow_raw),
.LOCKED (locked),
.PWRDWN (1'b0),
.RST (reset)
);
BUFG bufg_fb (
.I(clk_fb_out),
.O(clk_fb)
);
BUFG bufg_slow (
.I(clk_slow_raw),
.O(clk_slow)
);
wire rst_int = reset | ~locked;
reg [3:0] state;
always @(posedge clk_slow or posedge rst_int) begin
if (rst_int) state <= 4'b1111;
else state <= state - 1; // one step per 200 ns
end
reg [3:0] an;
always @(*) begin
case (state)
4'b1110: an = 4'b0111;
4'b1010: an = 4'b1011;
4'b0110: an = 4'b1101;
4'b0010: an = 4'b1110;
default: an = 4'b1111;
endcase
end
reg [3:0] digit;
always @(*) begin
case (state)
4'b0000, 4'b1111, 4'b1110, 4'b1101: digit = 4'h0;
4'b1100, 4'b1011, 4'b1010, 4'b1001: digit = 4'h1;
4'b1000, 4'b0111, 4'b0110, 4'b0101: digit = 4'h2;
default: digit = 4'h3;
endcase
end
wire [6:0] LED;
LEDdecoder LEDdecoder_inst (
.char(digit),
.LED (LED)
);
assign {an3, an2, an1, an0} = an;
assign a = ~LED[6];
assign b = ~LED[5];
assign c = ~LED[4];
assign d = ~LED[3];
assign e = ~LED[2];
assign f = ~LED[1];
assign g = ~LED[0];
assign dp = 1'b1;
endmodule