组合逻辑定义 wire
时序逻辑定义 reg 有个信号特例也用reg 没听清是什么
if判断,如果if后面只有一条语句,可以不加begin end ,如果有多条语句,要加begin end
如果是在always里面赋值,那么需要写reg 如果是在assign里面赋值,,就可以不写reg
例如 outout reg [3:0]led; 在always里面赋值
变量的赋值都是在时钟上升沿进行的 ,复位一般是高电平,所以在下降沿复位,复位之后是低电平,例如
不指定端口数据类型的话默认是wire型的
输入可以是reg或者wire型,输出必是wire型
//抓取上升沿代码固定写法
reg uart_en_d0;
reg uart_en_d1;
wire en_flag;
//捕获uart_en上升沿,得到一个时钟周期的脉冲信号
assign en_flag = (~uart_en_d1) & uart_en_d0;
//对发送使能信号uart_en延迟两个时钟周期
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n) begin
uart_en_d0 <= 1'b0;
uart_en_d1 <= 1'b0;
end
else begin
uart_en_d0 <= uart_en;
uart_en_d1 <= uart_en_d0;
end
end
//抓取下降沿固定写法,只是把取反符号换了下位置
//reg define
reg uart_rxd_d0;
reg uart_rxd_d1;
wire start_flag;
//捕获接收端口下降沿(起始位),得到一个时钟周期的脉冲信号
assign start_flag = uart_rxd_d1 & (~uart_rxd_d0);
//对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
//并行转串行
//根据发送数据计数器来给uart发送端口赋值
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n)
uart_txd <= 1'b1;
else if (tx_flag)
case(tx_cnt)
4'd0: uart_txd <= 1'b0; //起始位
4'd1: uart_txd <= tx_data[0]; //数据位最低位
4'd2: uart_txd <= tx_data[1];
4'd3: uart_txd <= tx_data[2];
4'd4: uart_txd <= tx_data[3];
4'd5: uart_txd <= tx_data[4];
4'd6: uart_txd <= tx_data[5];
4'd7: uart_txd <= tx_data[6];
4'd8: uart_txd <= tx_data[7]; //数据位最高位
4'd9: uart_txd <= 1'b1; //停止位
default: ;
endcase
else
uart_txd <= 1'b1; //空闲时发送端口为高电平
end
//串行转并行
//根据接收数据计数器来寄存uart接收端口数据
always @(posedge sys_clk or negedge sys_rst_n) begin
if ( !sys_rst_n)
rxdata <= 8'd0;
else if(rx_flag) //系统处于接收过程
if (clk_cnt == BPS_CNT/2) begin //判断系统时钟计数器计数到数据位中间
case ( rx_cnt )
4'd1 : rxdata[0] <= uart_rxd_d1; //寄存数据位最低位
4'd2 : rxdata[1] <= uart_rxd_d1;
4'd3 : rxdata[2] <= uart_rxd_d1;
4'd4 : rxdata[3] <= uart_rxd_d1;
4'd5 : rxdata[4] <= uart_rxd_d1;
4'd6 : rxdata[5] <= uart_rxd_d1;
4'd7 : rxdata[6] <= uart_rxd_d1;
4'd8 : rxdata[7] <= uart_rxd_d1; //寄存数据位最高位
default:;
endcase
end
else
rxdata <= rxdata;
else
rxdata <= 8'd0;
end
MODISM仿真
输入信号一般是reg型 输出信号一般是wire型
复位是低电平有效
停止位是高电平
锁相环默认高电平复位,低电平正常工作
ip核
1.ROM
输出端口寄存器存在的话,输出数据比地址数据晚两个时钟即第0个时钟输入地址,在第二个时钟周期输出数据,不存在的话晚一个时钟,即第0个时钟输入地址,第1个时钟输出数据
好的博客:
1.=是用在always@(*)块和assign语句中写组合逻辑电路的。<=只用在always@(posedge clk)块中用来写寄存器。always@(*)和assign之间没啥区别,都生成组合逻辑电路。只是有时组合逻辑比较复杂,用assign语句一句话写不完时会用always@(*)。区别就是always@(*)块中被赋值的信号要被定义成reg,而assign中被赋值的信号则必须是wire,但它们却都是生成组合逻辑电路。这就是Verilog一点不严谨的地方。不过这也没啥大问题,就是容易把初学者搞糊涂。有人喜欢把组合电路和时序电路在代码中分开来写,比如在always@(*)中写NextState = 一堆组合逻辑,然后再在always@(posedge clk)中只写 State <=NextState。不过我嫌这样写罗索,所以在我写的代码中就只会出现always@(posedge clk) 和assign。 作者:無限次元 https://www.bilibili.com/read/cv13109706 出处:bilibili
2.右移两位相当于处以4
去掉一位相当于处以2
移位两次相当于延迟三个时钟周期,如下面代码所示
//------------------------------------------
//lag 3 clocks signal sync
reg [2:0] per_frame_vsync_r;
reg [2:0] per_frame_href_r;
reg [2:0] per_frame_clken_r;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
per_frame_vsync_r <= 0;
per_frame_href_r <= 0;
per_frame_clken_r <= 0;
end
else
begin
per_frame_vsync_r <= {per_frame_vsync_r[1:0], per_frame_vsync};
per_frame_href_r <= {per_frame_href_r[1:0], per_frame_href};
per_frame_clken_r <= {per_frame_clken_r[1:0], per_frame_clken};
end
end
assign post_frame_vsync = per_frame_vsync_r[2];
assign post_frame_href = per_frame_href_r[2];
assign post_frame_clken = per_frame_clken_r[2];
偶数分频的分频系数N,通过时钟触发计数器计数,当计数器从0计数到N/2-1时,输出时钟进行翻转,以此循环下去,此处使用50分频,于是反转条件为50/2-1=24 得到1MHZ时钟
3.多条assign语句并行执行
阻塞赋值与非阻塞赋值
非阻塞赋值
a=1,b=2,c=3
begin
a<=b+1;
b<=a+1;
c<=a-1;
组合逻辑定义 wire
时序逻辑定义 reg 有个信号特例也用reg 没听清是什么
if判断,如果if后面只有一条语句,可以不加begin end ,如果有多条语句,要加begin end
如果是在always里面赋值,那么需要写reg 如果是在assign里面赋值,,就可以不写reg
例如 outout reg [3:0]led; 在always里面赋值
变量的赋值都是在时钟上升沿进行的 ,复位一般是高电平,所以在下降沿复位,复位之后是低电平,例如
不指定端口数据类型的话默认是wire型的
输入可以是reg或者wire型,输出必是wire型
//抓取上升沿代码固定写法
reg uart_en_d0;
reg uart_en_d1;
wire en_flag;
//捕获uart_en上升沿,得到一个时钟周期的脉冲信号
assign en_flag = (~uart_en_d1) & uart_en_d0;
//对发送使能信号uart_en延迟两个时钟周期
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n) begin
uart_en_d0 <= 1'b0;
uart_en_d1 <= 1'b0;
end
else begin
uart_en_d0 <= uart_en;
uart_en_d1 <= uart_en_d0;
end
end
//抓取下降沿固定写法,只是把取反符号换了下位置
//reg define
reg uart_rxd_d0;
reg uart_rxd_d1;
wire start_flag;
//捕获接收端口下降沿(起始位),得到一个时钟周期的脉冲信号
assign start_flag = uart_rxd_d1 & (~uart_rxd_d0);
//对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
//并行转串行
//根据发送数据计数器来给uart发送端口赋值
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n)
uart_txd <= 1'b1;
else if (tx_flag)
case(tx_cnt)
4'd0: uart_txd <= 1'b0; //起始位
4'd1: uart_txd <= tx_data[0]; //数据位最低位
4'd2: uart_txd <= tx_data[1];
4'd3: uart_txd <= tx_data[2];
4'd4: uart_txd <= tx_data[3];
4'd5: uart_txd <= tx_data[4];
4'd6: uart_txd <= tx_data[5];
4'd7: uart_txd <= tx_data[6];
4'd8: uart_txd <= tx_data[7]; //数据位最高位
4'd9: uart_txd <= 1'b1; //停止位
default: ;
endcase
else
uart_txd <= 1'b1; //空闲时发送端口为高电平
end
//串行转并行
//根据接收数据计数器来寄存uart接收端口数据
always @(posedge sys_clk or negedge sys_rst_n) begin
if ( !sys_rst_n)
rxdata <= 8'd0;
else if(rx_flag) //系统处于接收过程
if (clk_cnt == BPS_CNT/2) begin //判断系统时钟计数器计数到数据位中间
case ( rx_cnt )
4'd1 : rxdata[0] <= uart_rxd_d1; //寄存数据位最低位
4'd2 : rxdata[1] <= uart_rxd_d1;
4'd3 : rxdata[2] <= uart_rxd_d1;
4'd4 : rxdata[3] <= uart_rxd_d1;
4'd5 : rxdata[4] <= uart_rxd_d1;
4'd6 : rxdata[5] <= uart_rxd_d1;
4'd7 : rxdata[6] <= uart_rxd_d1;
4'd8 : rxdata[7] <= uart_rxd_d1; //寄存数据位最高位
default:;
endcase
end
else
rxdata <= rxdata;
else
rxdata <= 8'd0;
end
MODISM仿真
输入信号一般是reg型 输出信号一般是wire型
复位是低电平有效
停止位是高电平
锁相环默认高电平复位,低电平正常工作
ip核
1.ROM
输出端口寄存器存在的话,输出数据比地址数据晚两个时钟即第0个时钟输入地址,在第二个时钟周期输出数据,不存在的话晚一个时钟,即第0个时钟输入地址,第1个时钟输出数据
好的博客:
1.=是用在always@(*)块和assign语句中写组合逻辑电路的。<=只用在always@(posedge clk)块中用来写寄存器。always@(*)和assign之间没啥区别,都生成组合逻辑电路。只是有时组合逻辑比较复杂,用assign语句一句话写不完时会用always@(*)。区别就是always@(*)块中被赋值的信号要被定义成reg,而assign中被赋值的信号则必须是wire,但它们却都是生成组合逻辑电路。这就是Verilog一点不严谨的地方。不过这也没啥大问题,就是容易把初学者搞糊涂。有人喜欢把组合电路和时序电路在代码中分开来写,比如在always@(*)中写NextState = 一堆组合逻辑,然后再在always@(posedge clk)中只写 State <=NextState。不过我嫌这样写罗索,所以在我写的代码中就只会出现always@(posedge clk) 和assign。 作者:無限次元 https://www.bilibili.com/read/cv13109706 出处:bilibili
2.右移两位相当于处以4
去掉一位相当于处以2
移位两次相当于延迟三个时钟周期,如下面代码所示
//------------------------------------------
//lag 3 clocks signal sync
reg [2:0] per_frame_vsync_r;
reg [2:0] per_frame_href_r;
reg [2:0] per_frame_clken_r;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
per_frame_vsync_r <= 0;
per_frame_href_r <= 0;
per_frame_clken_r <= 0;
end
else
begin
per_frame_vsync_r <= {per_frame_vsync_r[1:0], per_frame_vsync};
per_frame_href_r <= {per_frame_href_r[1:0], per_frame_href};
per_frame_clken_r <= {per_frame_clken_r[1:0], per_frame_clken};
end
end
assign post_frame_vsync = per_frame_vsync_r[2];
assign post_frame_href = per_frame_href_r[2];
assign post_frame_clken = per_frame_clken_r[2];
偶数分频的分频系数N,通过时钟触发计数器计数,当计数器从0计数到N/2-1时,输出时钟进行翻转,以此循环下去,此处使用50分频,于是反转条件为50/2-1=24 得到1MHZ时钟
3.多条assign语句并行执行
阻塞赋值与非阻塞赋值
非阻塞赋值
a=1,b=2,c=3
begin
a<=b+1;
b<=a+1;
c<=a-1;
end
计算结果为:a=3,b=2,c=0
阻塞赋值
begin
a=b+1;
b=a+1;
c=a+1;
end
在上述代码中先执行b+1赋值给a后再执行a+1赋值给b以此类推
end
计算结果为:a=3,b=2,c=0
阻塞赋值
begin
a=b+1;
b=a+1;
c=a+1;
end
在上述代码中先执行b+1赋值给a后再执行a+1赋值给b以此类推