如何在FPGA上实现类似MCU的寄存器写敏感操作?
在FPGA上实现MCU风格的写敏感寄存器行为
在部分微控制器(MCU)的外设中,存在写敏感和读敏感寄存器。例如串口的TX寄存器,对其执行写操作会触发相应动作。我希望在FPGA上实现相同的行为,以下是我的设计代码及用于展示预期行为的测试台,请问如何达成该预期行为?
module serial #( parameter DATA_WIDTH = 8 ) ( input [DATA_WIDTH-1:0] TX, input clk, output reg data_had_been_written ); reg [DATA_WIDTH-1:0] tx_internal; always @(TX) begin data_had_been_written <= 1; tx_internal <= TX; //this operation must be synchronized with posedge of clk, I hadn't do this to be more briefly. end always @(negedge clk) begin data_had_been_written <= 0; end endmodule module tst_write_sensible; parameter DATA_WIDTH = 8; reg [DATA_WIDTH - 1:0] TX; reg clk; wire data_had_been_written; serial s ( .TX(TX), .clk(clk), .data_had_been_written(data_had_been_written) ); initial begin clk = 0; for (int i = 0; i < 20; i = i + 1) #1 clk = ~clk; end initial begin #2 TX = 4; //Posedge of data_had_been_written must be triggered here #2 TX = 5; //Posedge of data_had_been_written must be triggered here #2 TX = 5; //Posedge of data_had_been_written must be triggered here #2 TX = 6; //Posedge of data_had_been_written must be triggered here #2 TX = 7; //Posedge of data_had_been_written must be triggered here end endmodule
问题分析
原代码存在两个核心问题:
- 异步逻辑风险:使用
always @(TX)异步触发,FPGA中会引发亚稳态和信号毛刺,不符合同步设计规范。 - 重复写同值无法触发:当TX写入相同值时,
always @(TX)不会执行,无法满足测试台中重复写同值也要触发data_had_been_written的需求。
解决方案
方案1:规范同步设计(支持值变化触发)
如果仅需要检测TX值变化时触发动作,采用两级寄存器同步输入,避免亚稳态,同时检测值变化生成同步脉冲:
module serial #( parameter DATA_WIDTH = 8 ) ( input [DATA_WIDTH-1:0] TX, input clk, output reg data_had_been_written ); reg [DATA_WIDTH-1:0] tx_sync1; reg [DATA_WIDTH-1:0] tx_sync2; reg [DATA_WIDTH-1:0] tx_latched; // 两级寄存器同步输入,消除亚稳态 always @(posedge clk) begin tx_sync1 <= TX; tx_sync2 <= tx_sync1; end // 检测TX值变化,触发动作并锁存数据 always @(posedge clk) begin if (tx_sync2 != tx_latched) begin tx_latched <= tx_sync2; data_had_been_written <= 1'b1; end else begin data_had_been_written <= 1'b0; end end endmodule
方案2:支持重复写同值(添加写使能信号)
若需要重复写同值也触发动作,需添加写使能信号(符合真实总线接口设计),通过写使能边沿检测触发动作:
module serial #( parameter DATA_WIDTH = 8 ) ( input [DATA_WIDTH-1:0] TX, input TX_WR, // 写使能信号,上升沿有效 input clk, output reg data_had_been_written ); reg [DATA_WIDTH-1:0] tx_latched; reg tx_wr_sync1; reg tx_wr_sync2; // 同步写使能信号 always @(posedge clk) begin tx_wr_sync1 <= TX_WR; tx_wr_sync2 <= tx_wr_sync1; end // 检测写使能上升沿,触发动作并锁存数据 always @(posedge clk) begin if (tx_wr_sync2 && !tx_wr_sync1) begin tx_latched <= TX; data_had_been_written <= 1'b1; end else begin data_had_been_written <= 1'b0; end end endmodule
对应修改测试台,添加写使能信号:
module tst_write_sensible; parameter DATA_WIDTH = 8; reg [DATA_WIDTH - 1:0] TX; reg TX_WR; reg clk; wire data_had_been_written; serial s ( .TX(TX), .TX_WR(TX_WR), .clk(clk), .data_had_been_written(data_had_been_written) ); initial begin clk = 0; for (int i = 0; i < 20; i = i + 1) #1 clk = ~clk; end initial begin TX_WR = 0; #2 TX = 4; TX_WR = 1; #1 TX_WR = 0; #1 TX = 5; TX_WR = 1; #1 TX_WR = 0; #1 TX = 5; TX_WR = 1; #1 TX_WR = 0; // 重复写同值依然触发 #1 TX = 6; TX_WR = 1; #1 TX_WR = 0; #1 TX = 7; TX_WR = 1; #1 TX_WR = 0; end endmodule
方案3:适配原测试台(无写使能支持重复写)
如果必须基于原测试台逻辑,可利用TX赋值的时间规律(时钟negedge时刻赋值),在每个时钟周期生成同步脉冲:
module serial #( parameter DATA_WIDTH = 8 ) ( input [DATA_WIDTH-1:0] TX, input clk, output reg data_had_been_written ); reg [DATA_WIDTH-1:0] tx_negedge_sample; // 在时钟negedge采样TX值 always @(negedge clk) begin tx_negedge_sample <= TX; end // 在时钟posedge生成脉冲,对应每次TX赋值 always @(posedge clk) begin data_had_been_written <= 1'b1; end // 在时钟negedge清零脉冲 always @(negedge clk) begin data_had_been_written <= 1'b0; end endmodule
这个方案中,每个时钟周期的posedge会产生data_had_been_written脉冲,完全匹配原测试台的预期行为。
内容的提问来源于stack exchange,提问作者maestro
相关产品推荐
相关产品推荐

