一.通用编写技巧
Always 语句的理解 always @(posedge clk or negedge rst_n) begin
系统更新时刻(Self Define Word)
将Always的条件,定义为系统更新条件
系统更新时刻(Self Define): 将系统状态变化视为一步一步的,而不考虑全部时间段,常见的为CLK上升沿,体现于Always的条件中
negedge rst_n 为非常规条件,定义为异常更新时刻,可用作某种"捕获"
过程块中的信号一致性
Always语句仿真图
在时钟的上升沿或复位下降沿发生变化(系统更新时刻)
为何不以电平触发,而以边缘触发
电平触发意味着不断的扫描
边缘触发和脉冲触发某种程度上可以替代,即所有的脉冲信号也可触发边缘检测
并行性问题
代码示例
always @(posedge clk or negedge rst_n) begin
if(rst_n == 1'b0)
rw_cnt <= 1'b0;
else if(rw_cnt == 6'd63)
rw_cnt <= 1'b0;
else
rw_cnt <= rw_cnt + 1'b1;
end
//产生 RAM 写数据
always @(posedge clk or negedge rst_n) begin
if(rst_n == 1'b0)
ram_wr_data <= 1'b0;
else if(rw_cnt <= 6'd31) //在计数器的 0-31 范围内,RAM 写地址累加
ram_wr_data <= ram_wr_data + 1'b1;
else
ram_wr_data <= 1'b0 ;
end
Copy
Verilog
当一个信号同出现在不同的alwasys语句块时候,且always触发条件相同,比如代码中的rw_cnt
则以always触发条件前的一瞬间作为输出来运行
assign语句
电平检测→边缘检测互换定理(右表达式仅一个信号)
假设assign A = B
由于assign语句相当于逻辑门的直接连接
等效的为 A = B逻辑电平时刻相同
考虑所有信号时间太过于麻烦了,仅考虑B变化的时刻
当B变化时,可在B变换后的一瞬间采样作为表达式的输入
右表达式多个信号
可将assign信号变换的时刻为 其任一表达式信号变化的时,assign信号也跟着变化
但与always @(*)不同的是,变化瞬间的右边
两种设计逻辑
assign绑定CNT形式
不创建除了CNT之外的其他Reg变量
如果需要创建其他Reg,和CNT之间至少有一层wire隔离
二.组件编写实践
信号同步组件
信号同步代码
//对UART接收端口的数据延迟两个时钟周期
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n) begin
uart_rxd_d0 <= 1'b0;
uart_rxd_d1 <= 1'b0;
end
else begin
uart_rxd_d0 <= uart_rxd;
uart_rxd_d1 <= uart_rxd_d0;
end
end
Copy
Verilog
对于外界输入的信号,如果信号的变化与自身的时钟不同步,先需要进行同步化
同步化(Self Define): 即信号只能在系统更新时刻(Self Def)发生变化
若 uart_rxd 为非同步信号,则uart_rxd_d0为其同步化信号
若 uart_rxd 为同步信号,则uart_rxd_d0为其延迟一个时刻(拍)的信号
同步化信号的边缘检测组件
同步化信号的边缘检测
assign start_flag = uart_rxd_d1 & (~uart_rxd_d0);
//此为下降沿检测
Copy
Verilog
其输出为单位脉冲响应(离散形式)
脉冲延迟问题
图示
若原始信号uart_rxd刚好在系统更新时刻变换,则边缘检测脉冲在下一拍才出现
但是由于采用了assign 边缘检测组件始终同已同步的信号下降沿同时出现
三.模块编写指南
USART_ Receive/Send模块
USART时序
下降沿检测组件: 检测从Idle(高电平)到起始信号,并提供一个trans_flag标志,表示整个接受周期时候trans_flag=1
Bit周期计数器: 根据 系统更新频率(System CLK)/Bound_Rate = 每比特的计数周期
当前Bit计数器: 每次Bit周期计数器重装载,后到下一各比特加一
串行/并行转换: 唯一的不同