使用system verilog进行流水灯和VGA打印字符
目录
- **使用system verilog进行流水灯和VGA打印字符**
- **system verilog的优点**
- **VGA程序编写**
- VGA 控制器模块
- 字符生成模块
- 顶层模块
- 测试基准程序
- **效果**
- **流水灯程序设计**
- **效果**
- **总结**
system verilog的优点
面向对象编程:SystemVerilog 引入了面向对象的编程特性,如类(class)和继承,这有助于创建可重用的代码和设计模式。
增强的验证能力:SystemVerilog 支持断言(assertions)和覆盖率(coverage)分析,这些是验证复杂设计的关键工具。
并发建模:SystemVerilog 允许更自然地建模并发事件,通过使用 fork-join 语句来创建并行线程。
更丰富的数据类型:SystemVerilog 增加了多种数据类型,例如枚举(enum)、结构体(struct)、联合体(union)和数组,这些使得数据建模更加灵活和强大。
参数化编程:通过宏和参数化编程,SystemVerilog 允许更灵活的设计,可以更容易地创建可配置和可重用的设计组件。
改善的模块间连接:SystemVerilog 引入了接口(interface)的概念,简化了模块间的连接,并使得设计更加模块化。
更高级的建模能力:SystemVerilog 支持更高层次的抽象建模,有助于设计者在系统级别进行设计和验证。
随机化测试:SystemVerilog 支持随机化测试,可以自动生成测试向量,这有助于更全面地验证设计。
更有效的代码复用:通过封装和模块化,SystemVerilog 促进了代码的复用,提高了设计效率。
标准和兼容性:SystemVerilog 作为 IEEE 标准的一部分,确保了设计和验证方法的兼容性和标准化。
VGA程序编写
VGA 控制器模块
首先是 VGA 控制器模块,负责生成 VGA 信号和像素坐标。
module VGAController (
input logic clk, // 时钟
input logic reset, // 复位
output logic hsync, // 水平同步信号
output logic vsync, // 垂直同步信号
output logic [9:0] pixelX, // 像素 X 坐标
output logic [9:0] pixelY, // 像素 Y 坐标
output logic displayOn // 是否显示像素
);
// VGA 时序参数
parameter H_VISIBLE_AREA = 640;
parameter H_FRONT_PORCH = 16;
parameter H_SYNC_PULSE = 96;
parameter H_BACK_PORCH = 48;
parameter H_TOTAL = H_VISIBLE_AREA + H_FRONT_PORCH + H_SYNC_PULSE + H_BACK_PORCH;
parameter V_VISIBLE_AREA = 480;
parameter V_FRONT_PORCH = 10;
parameter V_SYNC_PULSE = 2;
parameter V_BACK_PORCH = 33;
parameter V_TOTAL = V_VISIBLE_AREA + V_FRONT_PORCH + V_SYNC_PULSE + V_BACK_PORCH;
// 水平和垂直计数器
logic [9:0] hCounter;
logic [9:0] vCounter;
// 水平计数器更新
always_ff @(posedge clk or posedge reset) begin
if (reset) begin
hCounter <= 0;
vCounter <= 0;
end else begin
if (hCounter == H_TOTAL - 1) begin
hCounter <= 0;
if (vCounter == V_TOTAL - 1) begin
vCounter <= 0;
end else begin
vCounter <= vCounter + 1;
end
end else begin
hCounter <= hCounter + 1;
end
end
end
// 生成同步信号
assign hsync = !(hCounter >= H_VISIBLE_AREA + H_FRONT_PORCH && hCounter < H_VISIBLE_AREA + H_FRONT_PORCH + H_SYNC_PULSE);
assign vsync = !(vCounter >= V_VISIBLE_AREA + V_FRONT_PORCH && vCounter < V_VISIBLE_AREA + V_FRONT_PORCH + V_SYNC_PULSE);
// 输出像素坐标和显示信号
assign pixelX = hCounter;
assign pixelY = vCounter;
assign displayOn = (hCounter < H_VISIBLE_AREA) && (vCounter < V_VISIBLE_AREA);
endmodule
字符生成模块
接下来是字符生成模块,根据像素坐标生成 “你好FPGA” 字符。
module CharGen (
input logic clk, // 时钟
input logic reset, // 复位
input logic [9:0] pixelX, // 像素 X 坐标
input logic [9:0] pixelY, // 像素 Y 坐标
input logic displayOn, // 是否显示像素
output logic [3:0] red, // 红色分量
output logic [3:0] green, // 绿色分量
output logic [3:0] blue // 蓝色分量
);
// 字符映射表,可以通过像素坐标获取相应的像素值
reg [63:0] charMap [0:6] = '{64'h7C121212127C00, 64'h42427E424200, 64'h7E0909090000, 64'h7E0909090600, 64'h3E414949493200, 64'h7E09097E0000, 64'h00000000000000};
// 计算字符索引和像素在字符中的位置
reg [3:0] charIndex;
reg [2:0] charRow, charCol;
reg [2:0] pixelXInChar, pixelYInChar;
reg charPixel;
always_ff @(posedge clk or posedge reset) begin
if (reset) begin
charIndex <= 0;
charRow <= 0;
charCol <= 0;
pixelXInChar <= 0;
pixelYInChar <= 0;
charPixel <= 0;
end else begin
// 计算当前字符索引
charCol <= pixelX / 8;
charRow <= pixelY / 12;
charIndex <= charCol + (charRow * 8);
// 计算像素在字符中的位置
pixelXInChar <= pixelX % 8;
pixelYInChar <= pixelY % 12;
// 获取当前像素值
charPixel <= (charMap[charIndex])[63 - (pixelYInChar * 8 + pixelXInChar)];
end
end
// 生成 RGB 信号
always_comb begin
case (charPixel)
1'b0: begin
red = 4'h0;
green = 4'h0;
blue = 4'h0;
end
1'b1: begin
red = 4'hF;
green = 4'hF;
blue = 4'hF;
end
default: begin
red = 4'h0;
green = 4'h0;
blue = 4'h0;
end
endcase
end
endmodule
顶层模块
以下是顶层模块,将 VGA 控制器和字符生成模块结合在一起,并输出到开发板上。
module Top (
input logic clk, // 时钟
input logic reset, // 复位
output logic [3:0] red, // 红色信号
output logic [3:0] green, // 绿色信号
output logic [3:0] blue, // 蓝色信号
output logic hsync, // 水平同步信号
output logic vsync // 垂直同步信号
);
// 实例化 VGA 控制器和字符生成模块
VGAController vgaController (
.clk(clk),
.reset(reset),
.hsync(hsync),
.vsync(vsync),
.pixelX(pixelX),
.pixelY(pixelY),
.displayOn(displayOn)
);
CharGen charGen (
.clk(clk),
.reset(reset),
.pixelX(pixelX),
.pixelY(pixelY),
.displayOn(displayOn),
.red(red),
.green(green),
.blue(blue)
);
endmodule
测试基准程序
最后是测试基准程序,用于测试顶层模块的功能。
module Top_tb;
// 时钟和复位信号
logic clk;
logic reset;
// VGA 控制器和字符生成模块的信号
logic hsync;
logic vsync;
logic [9:0] pixelX;
logic [9:0] pixelY;
logic displayOn;
logic [3:0] red;
logic [3:0] green;
logic [3:0] blue;
// 实例化被测试的模块
Top dut (
.clk(clk),
.reset(reset),
.hsync(hsync),
.vsync(vsync),
.red(red),
.green(green),
.blue(blue)
);
// 时钟生成
always #5 clk = ~clk;
// 复位信号生成
initial begin
reset = 1;
#10;
reset = 0;
#10000;
$finish;
end
// 输出测试结果
always @(posedge clk) begin
$display("hsync: %b, vsync: %b, red: %h, green: %h, blue: %h", hsync, vsync, red, green, blue);
end
endmodule
效果
流水灯程序设计
流水灯模块
module LED(
input clk,
input rst_n, // _n低电平有效
output logic [3:0] led
);
// 1.5s计数器
logic [27:0] cnt;
parameter int TIME_1_5S = 75_000_000;
// 由于cnt已经声明为logic类型,不需要额外的信号声明
logic add_cnt;
logic end_cnt;
// 赋值语句不需要阻塞赋值的begin-end结构
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt <= 28'd0;
end else if (add_cnt) begin
if (end_cnt) begin
cnt <= 28'd0;
end else begin
cnt <= cnt + 1;
end
end
end
// 使用always_comb声明组合逻辑
always_comb begin
add_cnt = 1'b1;
end_cnt = add_cnt && cnt == (TIME_1_5S - 1);
end
// 拼接法控制LED
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
led <= 4'b1110;
end else if (end_cnt) begin
led <= {led[2:0], led[3]}; // 流水灯
end
end
endmodule
顶层模块设计
`timescale 1ns/1ns
module LED_tb();
reg tb_clk;
reg tb_rst_n;
wire [3:0]tb_led;
LED #(.TIME_1_5S(750)) inst_LED(
.clk (tb_clk ) ,
.rst_n (tb_rst_n ) ,
.led (tb_led )
);
parameter cycle =20 ;//时钟周期为20ns
always#(cycle/2)tb_clk=~tb_clk;//过时钟周期一半取反
initial begin
tb_clk=1'b0;
tb_rst_n=1'b0;
#(cycle*3);
tb_rst_n=1'b1;
#(cycle*751*16);
$stop;
end
endmodule
效果
总结
这次对system verilog的编写让我对system verilog有了清晰的认识。