【发布时间】:2019-09-30 02:24:38
【问题描述】:
我正在 Verilog 中开发一个简单的视频信号定时模块,作为一个学习项目。我从之前的研究中了解到,每个reg 只能从一个always 块分配,因此我将系统安排为两个状态机块,然后是一个用于填充输出寄存器的块,如下所示:
module video_timing
(
input wire reset,
input wire clk,
output reg [15:0] x,
output reg [15:0] y,
output reg hsync,
output reg vsync,
output reg visible
);
// State constants for our two timing state machines (one horizontal, one vertical)
`define VIDEO_SYNC 2'd0
`define VIDEO_BACKPORCH 2'd1
`define VIDEO_ACTIVE 2'd2
`define VIDEO_FRONTPORCH 2'd3
// These settings are for 720p, assuming clk is running at 74.25 MHz
`define VIDEO_H_SYNC_PIXELS 16'd40
`define VIDEO_H_BP_PIXELS 16'd220
`define VIDEO_H_ACTIVE_PIXELS 16'd1280
`define VIDEO_H_FP_PIXELS 16'd110
`define VIDEO_H_SYNC_ACTIVE 1'b1
`define VIDEO_V_SYNC_LINES 16'd5
`define VIDEO_V_BP_LINES 16'd20
`define VIDEO_V_ACTIVE_LINES 16'd720
`define VIDEO_V_FP_LINES 16'd5
`define VIDEO_V_SYNC_ACTIVE 1'b1
reg [1:0] state_h;
reg [15:0] count_h; // 1-based so we will stop when count_h is the total pixels for the current state
reg inc_v = 1'b0;
reg [1:0] state_v;
reg [15:0] count_v; // 1-based so we will stop when count_v is the total lines for the current state
// Change outputs on clock.
// (These update one clock step behind everything else below, but that's
// okay because the lengths of all the periods are still correct.)
always @(posedge clk) begin
if (reset == 1'b1) begin
hsync <= ~`VIDEO_H_SYNC_ACTIVE;
vsync <= ~`VIDEO_V_SYNC_ACTIVE;
visible <= 1'b0;
x <= 16'd0;
y <= 16'd0;
end else begin
hsync <= (state_h == `VIDEO_SYNC) ^ (~`VIDEO_H_SYNC_ACTIVE);
vsync <= (state_v == `VIDEO_SYNC) ^ (~`VIDEO_V_SYNC_ACTIVE);
visible <= (state_h == `VIDEO_ACTIVE) && (state_v == `VIDEO_ACTIVE);
x <= count_h - 1;
y <= count_v - 1;
end
end
// Horizontal state machine
always @(posedge clk) begin
if (reset == 1'b1) begin
count_h <= 16'b1;
state_h <= `VIDEO_FRONTPORCH;
end else begin
inc_v <= 0;
count_h <= count_h + 16'd1;
case (state_h)
`VIDEO_SYNC: begin
if (count_h == `VIDEO_H_SYNC_PIXELS) begin
state_h <= `VIDEO_BACKPORCH;
count_h <= 16'b1;
end
end
`VIDEO_BACKPORCH: begin
if (count_h == `VIDEO_H_BP_PIXELS) begin
state_h <= `VIDEO_ACTIVE;
count_h <= 16'b1;
end
end
`VIDEO_ACTIVE: begin
if (count_h == `VIDEO_H_ACTIVE_PIXELS) begin
state_h <= `VIDEO_FRONTPORCH;
count_h <= 16'b1;
end
end
`VIDEO_FRONTPORCH: begin
if (count_h == `VIDEO_H_FP_PIXELS) begin
state_h <= `VIDEO_SYNC;
count_h <= 16'b1;
inc_v <= 1;
end
end
endcase
end
end
// Vertical state machine
always @(posedge clk) begin
if (reset == 1'b1) begin
count_v <= 16'b1;
state_v <= `VIDEO_FRONTPORCH;
end else begin
if (inc_v) begin
count_v <= count_v + 16'd1;
case (state_v)
`VIDEO_SYNC: begin
if (count_v == `VIDEO_V_SYNC_LINES) begin
state_v <= `VIDEO_BACKPORCH;
count_v <= 16'b1;
end
end
`VIDEO_BACKPORCH: begin
if (count_v == `VIDEO_V_BP_LINES) begin
state_v <= `VIDEO_ACTIVE;
count_v <= 16'b1;
end
end
`VIDEO_ACTIVE: begin
if (count_v == `VIDEO_V_ACTIVE_LINES) begin
state_v <= `VIDEO_FRONTPORCH;
count_v <= 16'b1;
end
end
`VIDEO_FRONTPORCH: begin
if (count_v == `VIDEO_V_FP_LINES) begin
state_v <= `VIDEO_SYNC;
count_v <= 16'b1;
end
end
endcase
end
end
end
endmodule
当尝试使用 IceStorm 工具链进行合成时,yosys 会警告hsync 有多个驱动程序:
Warning: multiple conflicting drivers for top.\timing.hsync:
port Q[0] of cell $techmap\timing.$procdff$109 ($adff)
port Q[0] of cell $techmap\timing.$procdff$110 ($adff)
nextpnr-ice40 随后在同样的问题上失败:
ERROR: multiple drivers on net 'P1B4' ($auto$simplemap.cc:496:simplemap_adff$375.Q and $auto$simplemap.cc:496:simplemap_adff$376.Q)
ERROR: Loading design failed.
我尝试了几种不同的排列来尝试解决这个问题,并提出了一些意见:
- 如果我将
case (reset)0:块中的分配(在第一个总是)更改为只是hsync <= 'VIDEO_H_SYNC_ACTIVE,那么结果是可综合的,并且确实在没有像我一样重置时不断断言hsync做出改变后的期望。 - 如果我完全删除
hsync <= (state_h == 'VIDEO_SYNC) ^ (~'VIDEO_H_SYNC_ACTIVE)行但在重置期间保留hsync <= ~'VIDEO_H_SYNC_ACTIVE则结果也是可合成的,并且hsync始终未断言。
(我在上面用' 替换了反引号,只是因为反引号是内联逐字文本的 Markdown 元字符。我在实际程序中使用反引号作为常量。)
这使我得出结论,hsync <= 右侧的表达式就是问题所在。但是我很困惑为什么vsync <= 之后的非常相似的行不会产生类似的错误:
hsync <= (state_h == `VIDEO_SYNC) ^ (~`VIDEO_H_SYNC_ACTIVE);
vsync <= (state_v == `VIDEO_SYNC) ^ (~`VIDEO_V_SYNC_ACTIVE);
在iverilog 的模拟下,该模块的行为符合我的预期。
我正在使用由 git sha1 a6a4349 构建的 Yosys 0.8 和 nextpnr。我也尝试升级到 Yosys 0.9 并得到相同的结果,但我怀疑是我的问题,而不是工具链。
【问题讨论】:
-
我建议你用
if (reset)替换你的case (reset)。我从未见过以这种方式与案例一起使用的重置。我怀疑综合工具也会混淆。 -
谢谢,@Oldfart。我尝试了一些不同的东西,所以忘了提到我还在那里尝试了
if和case;我最初写了if,但在其他地方看到了一些评论,表明一些合成工具只能理解case。不幸的是,这个特定的工具链似乎并没有产生不同的结果。我已经更新了问题,将修改后的版本包含在if语句中,结果相同。 -
我刚刚通过 Vivado 2018.2 运行了您的最新代码,没有出现任何错误。我开始怀疑你的工具有问题。
-
感谢@Oldfart!很高兴知道。也许我在
yosys中发现了一个极端情况或错误;我会看看我是否可以使用官方 Lattice 工具重现您的成功(因为我的目标是 Lattice 部分),如果可以,请参阅上游报告。 -
许多工具对异步重置的允许程度非常宽容。但是
if是唯一保证按照合成标准工作的方式。