《FPGA至简设计原理与应用》学习笔记2 —— FPGA至简设计原理

课程资源

视频:https://www.bilibili.com/video/BV14K4y1u7kH/

资料:https://www.aliyundrive.com/s/E9H7Mc5hqhu

第1章 高效编辑器GVIM

GVIM官方的四种操作模式

  1. 命令模式
  2. 插入模式
  3. 可视模式
  4. 正常模式

本课程至简设计法将GVIM分为三种模式

  1. 命令模式:只能看代码和发出命令,不能进行文本编辑
  2. 编辑模式:文本编辑
  3. 列操作模式:对多行的某些列进行操作

三种模式的转化方法

  1. 命令模式下的模式转换在这里插入图片描述

  2. 编辑模式下的模式转换在这里插入图片描述

  3. 列操作模式下的模式转换在这里插入图片描述

第1节 基本技巧

1.1 补全命令

编辑模式下使用,Ctrl+P

1.2 跳转命令

命令模式下使用,文件内跳转命令,有4种命令

  1. gg:跳转到文件的第一行
  2. G:跳转到文件的最后一行
  3. :100:跳转到文件的第100行
  4. ``:回到跳转前的那一行

1.3 搜索命令

命令模式下使用,有4种搜索命令

扫描二维码关注公众号,回复: 15194121 查看本文章
  1. *:光标移动到某一单词处,按下*会高亮本文件中所有该单词
  2. n:使用*命令或者/之后,按下n,会跳转到下一个匹配的单词处
  3. N:使用*命令或者/之后,按下N,会跳转到上一个匹配的单词处
  4. /ABC:在文本中查找所有ABC,并且高亮

1.4 删除复制命令

删除命令,在命令模式下使用

  1. dd:删除当前行
  2. d3d:删除从当前行开始一共3行

其他删除方法:鼠标选中之后,按下Delete。此方法在命令模式和编辑模式均可以使用。

复制命令

  1. yy:复制当前行
  2. y3y:复制从当前行开始一共3行

粘贴命令:p

第2节 高级技巧

2.1 替换命令

:%s/xx/yy/g:文件所有xx替换成yy

:%s/xx/yy/gc:文件所有xx替换成yy,但是每次替换需要按下y确认替换

:329,339s/cnt/cnt0/g:文件329行到339行中,所有cnt替换成cnt0

:329,339s/cnt/cnt0/gc,文件329行到339行中,所有cnt替换成cnt0,但是每次替换需要按下y确认替换

2.2 列删除方法

列删除的操作步骤

  1. 使用鼠标,选中某些行的某些列
  2. 按下Ctrl+q,进入列操作模式
  3. 按下Delete

进入列操作模式的另一种方法

  1. 在命令模式下,按下Ctrl+Q进入列操作模式
  2. 使用hjkl四个按键控制列块范围的选择

2.3 列插入方法

列插入的操作步骤

  1. 使用鼠标,选中某些行的某些列
  2. 按下Ctrl+q,进入列操作模式
  3. 按下大写的I
  4. 输入字符
  5. 按下Esc

列复制与列粘贴的操作步骤

  • 需要先对GVIM的配置文件进行更改,此操作只用做一次

    在GVIM的安装目录下的_vimrc中添加以下代码

    vmap <C-c> "+y
    vmap <C-x> "+c
    vmap <C-v> c<ESC>"+p
    imap <C-v> <C-r><C-o>+
    
  1. 使用鼠标,选中某些行的某些列
  2. 按下Ctrl+q,进入列操作模式
  3. Ctrl+C
  4. 光标移到需要复制的地方Ctrl+V

第2章 多用模板专注设计

第1节 至简设计法模板总表

在这里插入图片描述

第2节 模板演示

2.1 模块设计模板

module module_name(
    clk    ,
    rst_n  ,
    //其他信号,举例dout
    dout
    );

    //参数定义
    parameter      DATA_W =         8;

    //输入信号定义
    input               clk    ;
    input               rst_n  ;

    //输出信号定义
    output[DATA_W-1:0]  dout   ;

    //输出信号reg定义
    reg   [DATA_W-1:0]  dout   ;

    //中间信号定义
    reg                 signal1;

    //组合逻辑写法
    always@(*)begin
    end

    //时序逻辑写法
    always@(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
        end
        else begin
        end
    end

    endmodule

2.2 时序逻辑模板

// Shixu
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
    end
    else begin
    end
end

// Shixu2
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
    end
    else if()begin
    end
    else if()begin
    end
end

2.3 组合逻辑模板

// Zuhe
always  @(*)begin
end

// Zuhe2
always  @(*)begin
    if()begin
    end
    else begin
    end
end

2.4 计数器模板

// Jsq
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt <= 0;
    end
    else if(add_cnt)begin
        if(end_cnt)
            cnt <= 0;
        else
            cnt <= cnt + 1;
    end
end

assign add_cnt = ;       
assign end_cnt = add_cnt && cnt== ;

// Jsq2,仅仅支持到Jsq3
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt0 <= 0;
    end
    else if(add_cnt0)begin
        if(end_cnt0)
            cnt0 <= 0;
        else
            cnt0 <= cnt0 + 1;
    end
end

assign add_cnt0 = ;
assign end_cnt0 = add_cnt0 && cnt0== ;

always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        cnt1 <= 0;
    end
    else if(add_cnt1)begin
        if(end_cnt1)
            cnt1 <= 0;
        else
            cnt1 <= cnt1 + 1;
    end
end

assign add_cnt1 = end_cnt0;
assign end_cnt1 = add_cnt1 && cnt1== ;

// 命令模式下输入':call MDYJSQ()'
reg [ ():0]  cnt2     ;
wire        add_cnt2 ;
wire        end_cnt2 ;
always @(posedge clk or negedge rst_n) begin 
    if (rst_n==0) begin
        cnt2 <= 0; 
    end
    else if(add_cnt2) begin
        if(end_cnt2)
            cnt2 <= 0; 
        else
            cnt2 <= cnt2+1 ;
   end
end
assign add_cnt2 = ();
assign end_cnt2 = add_cnt2  && cnt2 == ()-1 ;

2.5 状态机模板

//四段式状态机

//第一段:同步时序always模块,格式化描述次态寄存器迁移到现态寄存器(不需更改)
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        state_c <= IDLE;
    end
    else begin
        state_c <= state_n;
    end
end

//第二段:组合逻辑always模块,描述状态转移条件判断
always@(*)begin
    case(state_c)
        IDLE:begin
            if(idl2s1_start)begin
                state_n = S1;
            end
            else begin
                state_n = state_c;
            end
        end
        S1:begin
            if(s12s2_start)begin
                state_n = S2;
            end
            else begin
                state_n = state_c;
            end
        end
        S2:begin
            if(s22s3_start)begin
                state_n = S3;
            end
            else begin
                state_n = state_c;
            end
        end
        default:begin
            state_n = IDLE;
        end
    endcase
end
//第三段:设计转移条件
assign idl2s1_start  = state_c==IDLE && ;
assign s12s2_start = state_c==S1    && ;
assign s22s3_start  = state_c==S2    && ;

//第四段:同步时序always模块,格式化描述寄存器输出(可有多个输出)
always  @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        out1 <=1'b0      //初始化
    end
    else if(state_c==S1)begin
        out1 <= 1'b1;
    end
    else begin
        out1 <= 1'b0;
    end
end

命令模式下,输入:call MDYZTJ(3,"S1","S2","S3",12,13,23,32,31)

parameter  S1 = 0 ;
parameter  S2 = 1 ;
parameter  S3 = 2 ;

always @(posedge clk or negedge rst_n) begin 
    if (rst_n==0) begin
        state_c <= S1 ;
    end
    else begin
        state_c <= state_n;
   end
end

always @(*) begin 
    case(state_c)  
        S1 :begin
            if(s12s2_start) 
                state_n = S2 ;
            else if(s12s3_start) 
                state_n = S3 ;
            else 
                state_n = state_c ;
        end
        S2 :begin
            if(s22s3_start) 
                state_n = S3 ;
            else 
                state_n = state_c ;
        end
        S3 :begin
            if(s32s2_start) 
                state_n = S2 ;
            else if(s32s1_start) 
                state_n = S1 ;
            else 
                state_n = state_c ;
        end
        default : state_n = S1 ;
    endcase
end

assign s12s2_start = state_c==S1 && ();
assign s12s3_start = state_c==S1 && ();
assign s22s3_start = state_c==S2 && ();
assign s32s2_start = state_c==S3 && ();
assign s32s1_start = state_c==S3 && ();

2.6 测试文件模板

`timescale 1 ns/1 ns

module testbench_name();

//时钟和复位
reg clk  ;
reg rst_n;

//uut的输入信号
reg[3:0]  din0  ;
reg       din1  ;


//uut的输出信号
wire      dout0;
wire[4:0] dout1;


//时钟周期,单位为ns,可在此修改时钟周期。
parameter CYCLE    = 20;

//复位时间,此时表示复位3个时钟周期的时间。
parameter RST_TIME = 3 ;

//待测试的模块例化
module_name uut(
    .clk          (clk     ), 
    .rst_n        (rst_n   ),
    .din0         (din0    ),
    .din1         (din1    ),
    .dout0        (dout0   ),
    .dout1        (dout1   )

);


//生成本地时钟50M
initial begin
    clk = 0;
    forever
    #(CYCLE/2)
    clk=~clk;
end

//产生复位信号
initial begin
    rst_n = 1;
    #2;
    rst_n = 0;
    #(CYCLE*RST_TIME);
    rst_n = 1;
end

//输入信号din0赋值方式
initial begin
    #1;
    //赋初值
    din0 = 0;
    #(10*CYCLE);
    //开始赋值

end

//输入信号din1赋值方式
initial begin
    #1;
    //赋初值
    din1 = 0;
    #(10*CYCLE);
    //开始赋值

end



endmodule

2.7 reg类型代码模板

// Reg4
reg   [    3: 0]         ;

2.8 wire类型代码模板

// Wire4
wire  [    3: 0]         ;

2.9 输入信号定义模板

// Input4
input [    3: 0]         ;

2.10 输出信号定义模板

// Output4
output[    3: 0]         ;

2.11 initial语句模板

initial begin
    #1;
end

第3章 FPGA至简设计法规范

第1节 波形图规则

针对波形的规则:时钟上升沿看信号,看到的是信号变化前的值。

该方法使用的前提是:所有信号都是同步信号且波形是理想的波形。

第2节 计数器规范

计数器设计是FPGA设计的核心,计数器架构是FPGA设计最常用的架构之一,也是至简设计法的核心部分。

计数器一般包括三段

  1. 计数器的always语句
  2. 加1条件的定义
  3. 结束条件的定义

计数器模板如下

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt <= 0;
    end
    else if(add_cnt)begin	// 加1条件
        if(end_cnt)			// 结束条件
            cnt <= 0;
        else
            cnt <= cnt + 1;
    end
end

assign add_cnt = ;       				// 此处定义加1条件
assign end_cnt = add_cnt && cnt== ;		// 此处定义结束条件

计数器设计规则1:计数器要考虑三要素:初值、加1条件和结束值。

  • 初值:计数器的默认值或者开始计数的值
  • 加1条件:计数器执行加1的条件
  • 结束值:计数器计数周期的最后一个值

计数器设计规则2:计数初始值必须为0

计数器设计规则3:只有加1条件有效时,才能表示计数器的计数值

  • add_cnt&&cnt==x-1表示计数器计数到 x个。这是结束条件为什么要像模板那样设计的原因。这个表达式代表某一个瞬间。
  • cnt==x-1不能表示计数器计数到x个。这个表达式代表一段时间。

计数器设计规则4:结束条件必须同时满足加1条件,且结束值必须是x-1的形式;

计数器设计规则5:当取某个数时,assign形式必须为:(加1条件)&&(cnt==计数值-1);

计数器设计规则6:计数结束后,计数器的值必须恢复默认值 0;

计数器设计规则7:若需要限定范围,推荐使用“>=”和“<”两种符号;

计数器设计规则8:设计步骤是,先写计数器的always段,条件用名字代替;然后用assign写出加1条件;最后用assign写出结束条件。

计数器设计规则9:加1条件必须与计数器严格对齐,其他信号一律与计数器对齐。

在这里插入图片描述

计数器设计规则10:命名必须符合规范,比如:add_cnt表示加1条件;end_cnt表示结束条件。

计数器设计规则11:减 1计数器暂时不用。

要用减1计数器也应该像如下方式使用

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt <= 0;
    end
    else if(add_cnt)begin	// 加1条件
        if(end_cnt)			// 结束条件
            cnt <= 0;
        else
            cnt <= cnt + 1;
    end
end

assign add_cnt = ;       				// 此处定义加1条件
assign end_cnt = add_cnt && cnt== 8-1;		// 此处定义结束条件

assign cnt_1 = 7-cnt

第3节 状态机规范

状态机时数字电路设计中的一个非常重要组成部分,也是贯穿于整个设计始终的最基本设计思想和设计方法。

其实计数器本质上也可以认为是一个状态机,只不过计数器用数字来区分不同状态而已。

何时使用计数器,何时使用状态机呢?

  • 如果是顺序处理或者简单的控制流程,例如其步骤是0->1->2->3->0,这个使用用计数器实现是最便捷的。
  • 在复杂的流程控制场合,例如其步骤是0->1->5->2->4,其跳转顺序是乱序的时候,就应该利用状态机来设计。

规范的状态机代码可以极大地提高设计效率,在减少状态出错可能的同时缩短调试时间,从而设计出稳健的系统。

状态机的模板如下

//四段式状态机

//第一段:同步时序always模块,格式化描述次态寄存器迁移到现态寄存器(不需更改)
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        state_c <= IDLE;
    end
    else begin
        state_c <= state_n;
    end
end

//第二段:组合逻辑always模块,描述状态转移条件判断
always@(*)begin
    case(state_c)
        IDLE:begin
            if(idl2s1_start)begin
                state_n = S1;
            end
            else begin
                state_n = state_c;
            end
        end
        S1:begin
            if(s12s2_start)begin
                state_n = S2;
            end
            else begin
                state_n = state_c;
            end
        end
        S2:begin
            if(s22s3_start)begin
                state_n = S3;
            end
            else begin
                state_n = state_c;
            end
        end
        default:begin
            state_n = IDLE;
        end
    endcase
end
//第三段:设计转移条件
assign idl2s1_start  = state_c==IDLE && ;
assign s12s2_start = state_c==S1    && ;
assign s22s3_start  = state_c==S2    && ;

//第四段:同步时序always模块,格式化描述寄存器输出(可有多个输出)
always  @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        out1 <=1'b0      //初始化
    end
    else if(state_c==S1)begin
        out1 <= 1'b1;
    end
    else begin
        out1 <= 1'b0;
    end
end

状态机规则1:使用四段式写法。

状态机规则2:四段式状态机第一段写法不变。

状态机规则3:第二段的状态转移条件用信号来表示。

状态机规则4:用 assign将状态转移条件写成 XX2XX_start的形式。

状态机规则5:assign定义状态转移条件信号时,必须加上当前状态。

  • 确实有冗余,但是由于转移条件从always语句分离出去了,加上当前状态之后,可以方便看出此转移条件是属于哪个状态的,更方便定位和调试。

状态机规则6:状态不变时使用 state_n = state_c。

第4节 接口规范

至简设计法在实际项目经验中总结得出了一般模块端口的信号规范。

在这里插入图片描述

第5节 FIFO规范

FIFO,即先入先出队列。

在计算机中先进先出队列是一种传统的按序执行方法,先完成并引退先进入的指令,随后跟着执行第二条指令。

在数字电路设计中提到的FIFO实际是指FIFO存储器,其主要应用于数据缓存和异步处理。FIFO存储器缓存数据也遵循先进先出的原则。

FIFO本质上是一个RAM,其与普通存储器的区别是没有外部读/写地址线。FIFO存储器只能顺序的写入数据,再顺序的读取数据,其数据地址由内部读写指针自动加1完成

本教程没有使用到FIFO存储器,但是FIFO存储器再实际工程中有大量的应用。

FIFO规则 1:使用 Show-ahead读模式。

  • FIFO读操作一般有两种模式:Normal和Show-ahead模式。

  • Normal模式是读使能生效后的下一拍读出相应的数据。如下图所示:

    在这里插入图片描述

  • Show-ahead模式是先进行数据输出,在读使能有效时对FIFO输出数据进行更新。即FIFO中的第一个数据输出在总线上,在读使能信号到来的下一拍直接输出第二个数据。如下图所示

    在这里插入图片描述

FIFO规则 2:读写隔离规则。

  • FIFO的读写控制是独立的,两者之间除了共同FIFO进行信息操作外,不能有任何信息传递

  • 因此既不能根据FIFO的读状态或者读出的数据来决定写行为,也不能根据FIFO的写状态和写入的数据来决定都行为。在这里插入图片描述

  • 举例

    在这里插入图片描述

FIFO规则 3:读使能必须判断空状态,并且用组合逻辑产生。

FIFO规则 4:处理报文时,把指示信号与数据一起存入 FIFO。

FIFO规则 5:读写时钟不同时,必须用异步 FIFO。

  • FIFO按时钟可以分为同步FIFO和异步FIFO
  • 同步FIFO:读时钟和写时钟都相同的FIFO。同步FIFO内部没有异步处理,因此结构简单,资源占用较少。
  • 异步FIFO指:都市中和写时钟不同的FIFO。异步FIFO内部有专门的异步处理电路。处理读、写信号的交互,因此异步FIFO结构复杂,占用资源较大。

猜你喜欢

转载自blog.csdn.net/inv1796915552/article/details/129444294
今日推荐