FPGA学习笔记

组合逻辑定义  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以此类推
 

你可能感兴趣的:(fpga开发,学习)