fpga项目:初始化序列的verilog实现方法 - minichao9901/TangNano-20k-Zynq-7020 GitHub Wiki

目的

  • 逐条解析并执行指令,通过spi/i2c等接口执行指令。
  • 其中包括delay指令,通过cnt实现。由cnt_start/cnt_end来交互
  • spi/i2c通过xfer_start/xfer_end来交互
时序顺序构想为:
第1次(普通指令):cnt_start->cnt_end->state=1, rom_inex++->data->xfer_start->xfer_end
第2次(普通指令):xfer_end->rom_inex++->data->xfer_start->xfer_end
第n次(执行延时指令):xfer_end->rom_index++->data->cnt_start->state=2->cnt_end
第n+1次(延时指令结束):cnt_end->rom_inex++->data->xfer_start->xfer_end
第n+2次(普通指令):xfer_end->rom_inex++->data->xfer_start->xfer_end
最后一次(普通指令):xfer_end->rom_index++->state=0, data->xfer_start->xfer_end

线性序列机时序图

image

每一轮传输的发起,可能是xfer_end,也可能是cnt_end

实现时序波形图

代码

module tb3;
reg clk, rst_n;
initial clk=0;
always #10 clk=~clk;
initial begin
    rst_n=0;
    #1000;
    rst_n=1;
end 

//1)
parameter DLY_CNT_MAX=100;
parameter ARG_NMAX=12;
reg [15:0] dly_cnt;


//2)
reg [2:0] state;
always @(posedge clk or negedge rst_n)
if(rst_n==0) begin
    state<=0;
    dly_cnt<=0;
end
else case(state)
    0:  if(dly_cnt==DLY_CNT_MAX-1) begin
            dly_cnt<=0;
            state<=1;
        end
        else if(rom_index<ARG_NMAX-1)
            dly_cnt<=dly_cnt+1;  
    1: if(rom_index==ARG_NMAX-1)
        state<=0;
       else if(cnt_start) 
        state<=2;
       else
        state<=1;
    2: if(rom_index==ARG_NMAX-1)
        state<=0;
        else if(dly_cnt==length-1) begin
            dly_cnt<=0;
            state<=1;
        end
        else
            dly_cnt<=dly_cnt+1;
    default: state<=0;
  endcase
  

//3)
reg [7:0] rom_index;
always @(posedge clk or negedge rst_n)
if(rst_n==0)
    rom_index<=8'h00;
else if((dly_cnt==DLY_CNT_MAX-1 && state==0)
        || (dly_cnt==length-1 && state==2)
        || xfer_end && state==1)
    rom_index<=rom_index+1;
//else if(rom_index==ARG_NMAX-1)
//    rom_index<=8'h00;


reg [31:0] rom_data;
reg [7:0] cmd;
reg [7:0] length;
always @(posedge clk or negedge rst_n)
if(rst_n==0)
    rom_data<=32'h00;
else
    case(rom_index)
        1: rom_data  <=32'h12345678;
        2: rom_data  <=32'h23456789;
        3: rom_data  <=32'h3456789a;
        4: rom_data  <=32'h456789ab; 
        5: rom_data  <=32'hffabcdef;            
        6: rom_data  <=32'h56789abc;
        7: rom_data  <=32'h6789abcd;
        8: rom_data  <=32'h789abcde;
        9: rom_data  <=32'h89abcdef; 
        10: rom_data <=32'hffabcdef;
        11: rom_data  <=32'h89abcdef;           
        default: rom_data<=32'h00;       
    endcase   
    
assign cmd=rom_data[31:24];
assign length=rom_data[23:16];

//4)
reg [7:0] cmd_s;
wire xfer_start;
reg xfer_end;
always @(posedge clk or negedge rst_n)
if(rst_n==0)
    cmd_s<=8'h00;
else
    cmd_s<=cmd;
    
assign xfer_start= (cmd!=cmd_s) && (cmd!=8'hff);
assign cnt_start= (cmd!=cmd_s) && (cmd==8'hff);

initial begin
    xfer_end=0;
    repeat(ARG_NMAX) begin
//    while(rom_index<=ARG_NMAX-1) begin
        wait(xfer_start==1);
        #200;
         @(posedge clk) #1 xfer_end=1;
         @(posedge clk) #1 xfer_end=0;         
    end
end

endmodule

波形分析

image

整体

image

第一次传输

image

第一次执行dly

image

第一次dly结束

image

最后一次命令执行

代码修改(将计数器独立出来)

虽然将计数器独立出来,代码会更长一点。但实际上,更容易维护,在思路上更清晰。因为我们在设计的时候,往往是先设计状态机。通过先假设几个交互信号(cnt_start/cnt_end, xfer_start/xfer_end),先状态机代码确定下来。然后再去实现计数器,和这些交互信号。这种思路是,先整体,再局部。

module tb4;
reg clk, rst_n;
initial clk=0;
always #10 clk=~clk;
initial begin
    rst_n=0;
    #1000;
    rst_n=1;
end 

//1)
parameter DLY_CNT_MAX=100;
parameter ARG_NMAX=13;
reg [15:0] dly_cnt;

always @(posedge clk or negedge rst_n)
if(rst_n==0) begin
    dly_cnt<=0;
end
else case(state)
    0:  if(cnt_end)
            dly_cnt<=0;
        else if(rom_index<ARG_NMAX-1)
            dly_cnt<=dly_cnt+1;  
    2: if(cnt_end)
            dly_cnt<=0;
        else
            dly_cnt<=dly_cnt+1;
    default: dly_cnt<=0;
  endcase
 
assign cnt_end=(state==0 && dly_cnt==DLY_CNT_MAX-1) || (state==2 && dly_cnt==length-1);  

//2)
reg [2:0] state;
always @(posedge clk or negedge rst_n)
if(rst_n==0) begin
    state<=0;
end
else case(state)
    0:  if(cnt_end)
            state<=1;
    1: if(rom_index==ARG_NMAX-1)
        state<=0;
       else if(cnt_start) 
        state<=2;
    2: if(rom_index==ARG_NMAX-1)
        state<=0;
        else if(cnt_end)
            state<=1;
    default: state<=0;
  endcase
    
  
//3)
reg [7:0] rom_index;
always @(posedge clk or negedge rst_n)
if(rst_n==0)
    rom_index<=8'h00;
else if(cnt_end|| xfer_end)
    rom_index<=rom_index+1;

//4)
reg [31:0] rom_data;
reg [7:0] cmd;
reg [7:0] length;
always @(posedge clk or negedge rst_n)
if(rst_n==0)
    rom_data<=32'h00;
else
    case(rom_index)
        1: rom_data  <=32'h12345678;
        2: rom_data  <=32'h23456789;
        3: rom_data  <=32'h3456789a;
        4: rom_data  <=32'h456789ab; 
        5: rom_data  <=32'hffabcdef;            
        6: rom_data  <=32'h56789abc;
        7: rom_data  <=32'h6789abcd;
        8: rom_data  <=32'h789abcde;
        9: rom_data  <=32'h89abcdef; 
        10: rom_data <=32'hffabcdef;
        11: rom_data  <=32'h89abcdef;    
        12: rom_data  <=32'hffabcdef;                  
        default: rom_data<=32'h00;       
    endcase   
    
assign cmd=rom_data[31:24];
assign length=rom_data[23:16];

//5)
reg [7:0] cmd_s;
wire xfer_start;
reg xfer_end;
always @(posedge clk or negedge rst_n)
if(rst_n==0)
    cmd_s<=8'h00;
else
    cmd_s<=cmd;
    
assign xfer_start= (cmd!=cmd_s) && (cmd!=8'hff);
assign cnt_start= (cmd!=cmd_s) && (cmd==8'hff);

//6)
initial begin
    xfer_end=0;
    repeat(ARG_NMAX) begin
//    while(rom_index<=ARG_NMAX-1) begin
        wait(xfer_start==1);
        #200;
         @(posedge clk) #1 xfer_end=1;
         @(posedge clk) #1 xfer_end=0;         
    end
end

endmodule

代码修改(将rom单独拎出来)

module tb4;
reg clk, rst_n;
initial clk=0;
always #10 clk=~clk;
initial begin
    rst_n=0;
    #1000;
    rst_n=1;
end 

//1)
parameter DLY_CNT_MAX=100;
parameter ARG_NMAX=13;
reg [15:0] dly_cnt;

always @(posedge clk or negedge rst_n)
if(rst_n==0) begin
    dly_cnt<=0;
end
else case(state)
    0:  if(cnt_end)
            dly_cnt<=0;
        else if(rom_index<ARG_NMAX-1)
            dly_cnt<=dly_cnt+1;  
    2: if(cnt_end)
            dly_cnt<=0;
        else
            dly_cnt<=dly_cnt+1;
    default: dly_cnt<=0;
  endcase
 
assign cnt_end=(state==0 && dly_cnt==DLY_CNT_MAX-1) || (state==2 && dly_cnt==length-1);  

//2)
reg [2:0] state;
always @(posedge clk or negedge rst_n)
if(rst_n==0) begin
    state<=0;
end
else case(state)
    0:  if(cnt_end)
            state<=1;
    1: if(rom_index==ARG_NMAX-1)
        state<=0;
       else if(cnt_start) 
        state<=2;
    2: if(rom_index==ARG_NMAX-1)
        state<=0;
        else if(cnt_end)
            state<=1;
    default: state<=0;
  endcase
    
  
//3)
reg [7:0] rom_index;
always @(posedge clk or negedge rst_n)
if(rst_n==0)
    rom_index<=8'h00;
else if(cnt_end|| xfer_end)
    rom_index<=rom_index+1;

//4)
reg [0: 32*(ARG_NMAX-1)-1] rom={
        32'h12345678,
        32'h23456789,
        32'h3456789a,
        32'h456789ab, 
        32'hffabcdef,            
        32'h56789abc,
        32'h6789abcd,
        32'h789abcde,
        32'h89abcdef, 
        32'hffabcdef,
        32'h89abcdef,    
        32'hffabcdef
        };   

reg [31:0] rom_data;
reg [7:0] cmd;
reg [7:0] length;
always @(posedge clk or negedge rst_n)
if(rst_n==0)
    rom_data<=32'h00;
else if(rom_index>=1 && rom_index<ARG_NMAX)
    rom_data<=rom[32*(rom_index-1) +:32]; 
    
assign cmd=rom_data[31:24];
assign length=rom_data[23:16];

//5)
reg [7:0] cmd_s;
wire xfer_start;
reg xfer_end;
always @(posedge clk or negedge rst_n)
if(rst_n==0)
    cmd_s<=8'h00;
else
    cmd_s<=cmd;
    
assign xfer_start= (cmd!=cmd_s) && (cmd!=8'hff);
assign cnt_start= (cmd!=cmd_s) && (cmd==8'hff);

//6)
initial begin
    xfer_end=0;
    repeat(ARG_NMAX) begin
//    while(rom_index<=ARG_NMAX-1) begin
        wait(xfer_start==1);
        #200;
         @(posedge clk) #1 xfer_end=1;
         @(posedge clk) #1 xfer_end=0;         
    end
end

endmodule