我用Verilog写了我的第一个模块。其目的是维护两个计数器并发出与VGA的HSync和VSync相对应的信号,以及视频控制器用于消隐间隔的HBlank和VBlank脉冲。唯一的输入是25.175 MHz的时钟信号。所有的时间信息都是从这里获取的。
module sync (clk, hblank, hsync, vblank, vsync) ;
input clk;
output reg hblank = 0, hsync = 1, vblank = 0, vsync = 1;
reg [9:0] hcnt = 0, vcnt = 0;
always @(posedge clk) begin
case (hcnt)
640: hblank <= 1;
656: hsync <= 0;
752: hsync <= 1;
800: begin
hblank <= 0;
hcnt <= 0;
vcnt <= vcnt + 1;
end
endcase
case (vcnt)
480: vblank <= 1;
490: vsync <= 0;
492: vsync <= 1;
525: begin
vblank <= 0;
vcnt <= 0;
end
endcase
hcnt <= hcnt + 1;
end
endmodule发布于 2019-07-24 00:19:55
如果您已经运行仿真或加载到FPGA,您会注意到您没有得到预期的行为。在加载到FPGA之前,运行仿真并查看波形。
在始终块的底部有hcnt <= hcnt + 1;,这将覆盖hcnt <= 0;,这不是您想要的。<=是一个非阻塞的赋值,这意味着它将被立即评估,但值将在时间步骤结束之前不会被更新。秩序很重要。
最简单的解决方案是将hcnt <= hcnt + 1;移到case语句之上。
请注意,您的hcnt正在计数从0到800,即801时钟。您可能需要考虑从每种情况中减去1,或者启动/重置hcnt (和vcnt)为1,而不是0。
您的vcnt将仅为525一个时钟,这看起来不是故意的。考虑将其移动到case(hcnt)的S条件800内。
我将建议添加一个重置输入。并建议使用ANSI头,非ANSI是必需的Verilog-95和前IEEE1364.自Verilog-2001以来,ANSI风格是首选的,主要是因为它减少了输入量。
以下是我的建议。注意,我还没有测试是否满足了所有的功能需求(这应该在您的testbench中完成)。
module sync (
input clk, rst_n, // <-- ANSI header
output reg hblank, hsync, vblank, vsync
);
reg [9:0] hcnt, vcnt;
always @(posedge clk) begin
if (!rst_n) begin // <-- synchronous reset logic
hblank <= 1'b0;
hsync <= 1'b1;
vblank <= 1'b0;
vsync <= 1'b1;
hcnt <= 10'h001; // <-- init as 1 so case index doesn't need to change
vcnt <= 10'h001; // <-- same as vcnt
end
else begin
hcnt <= hcnt + 10'h001; // <-- default assignment, will be updated after the clock
case (hcnt) // <-- uses the sampled value, not the result of the above line
10'd640: hblank <= 1'b1;
10'd656: hsync <= 1'b0;
10'd752: hsync <= 1'b1;
10'd800: begin
hblank <= 1'b0;
hcnt <= 10'h001; // <-- reset as 1, last assignment wins
vcnt <= vcnt + 10'h001;
case (vcnt)
10'd480: vblank <= 1'b1;
10'd490: vsync <= 1'b0;
10'd492: vsync <= 1'b1;
10'd525: begin
vblank <= 1'b0;
vcnt <= 10'h001; // <-- reset as 1, last assignment wins
end
endcase
end
endcase
end
end
endmodule您可能需要考虑使用2路块编码风格。它确实需要更多的代码行--小设计(通常减少大型/复杂设计的代码行数)。主要的好处是您可以访问当前状态和失败的下一个状态。
// sequential logic (uses non-blocking assignment and is synchronous)
always @(posedge clk) begin
if (!rst_n) begin
hblank <= 1'b0;
hsync <= 1'b1;
vblank <= 1'b0;
vsync <= 1'b1;
hcnt <= 10'h001;
vcnt <= 10'h001;
end
else begin
hblank <= next_hblank;
hsync <= next_hsync;
vblank <= next_vblank;
vsync <= next_vsync;
hcnt <= next_hcnt;
vcnt <= next_vcnt;
end
end
// combinational logic (uses blocking assignment and is asynchronous)
always @* begin
next_hblank = hblank; // <-- default keep previous
next_hsync = hsync;
next_vblank = vblank;
next_vsync = vsync;
next_hcnt = hcnt + 10'h001; // <-- default increment
next_vcnt = vcnt;
// calc next values, update as needed
case (hcnt)
10'd640: next_hblank = 1'b1;
10'd656: next_hsync = 1'b0;
10'd752: next_hsync = 1'b1;
10'd800: begin
next_hblank = 1'b0;
next_hcnt = 10'h001;
next_vcnt = vcnt + 10'h001;
case (vcnt)
10'd480: next_vblank = 1'b1;
10'd490: next_vsync = 1'b0;
10'd492: next_vsync = 1'b1;
10'd525: begin
next_vblank = 1'b0;
next_vcnt = 10'h001;
end
endcase
end
endcase
endhttps://codereview.stackexchange.com/questions/223996
复制相似问题