夏宇闻复习笔记第15章:序列检测&并转串时序逻辑实例

文章目录

      • 15.1 序列检测
        • 1101序列检测
        • 10010序列检测
      • 15.2 并行转特殊串行模块
        • M0模块:p2sda
        • M1模块:out16hi
        • M2模块:sigdata
        • top模块

15.1 序列检测

1101序列检测

/******************************p215 15.2 1101序列检测********************************/
/*----------------------------------
Filename: squence_detector.v
Function: 检测序列1101(可重叠检测)
Author: Coin
Date: 2020-07-19 15:06:46
-----------------------------------*/
module squence_detector(clk, reset, d, y);
	//定义输入输出端口
	input clk, reset, d;
	output y;
	
	//内部寄存器及连线定义
	reg [4 : 0] current_state, next_state;
	wire y;
	
	//状态定义
    //其实化简状态图后,s4与s1的状态转移去向和输出均相同,可以合并
	parameter s0 = 5'b00000, s1 = 5'b00010, s2 = 5'b00100, s3 = 5'b01000, s4 = 5'b10000;
	
	//时序逻辑实现状态转移
	always@(posedge clk or negedge reset)
	begin
		if(!reset) current_state <= s0;
		else current_state <= next_state;
	end
	
	//组合逻辑实现状态转移条件判断
	always@(current_state or d)
	begin
        case(current_state)
            s0: next_state = d ? s1 : s0;
            s1: next_state = d ? s2 : s0;
            s2: next_state = d ? s2 : s3;
            s3: next_state = d ? s4 : s0;
            s4: next_state = d ? s1 : s0;
        endcase
	end
	
	//时序逻辑实现输出,Moore机,y的变化比current_state的变化晚一拍
	always@(posedge clk or negedge reset)
	begin
        if(!reset)  y <= 1'b0;
        else
            case(current_state)
                s0: y <= 1'b0;
                s1: y <= 1'b0;
                s2: y <= 1'b0;
                s3: y <= 1'b0;
                s4: y <= 1'b1;
            endcase
	end
	
	//本例中由于只有一个输出,也可用assign语句实现,y的变化与current_state同步
	assign y = (current_state == s4) ? 1 : 0;
	
endmodule

/*---------------------------------------
Filename: squence_detector_t.v
Function: 1101(可重叠)序列检测器测试模块
Author: Coin
Date: 2020-07-19 16:34:22
---------------------------------------*/
`timescale 1ns/1ns
`define halfperiod 5
module squence_detector_t;

	//定义内部连线及寄存器
	reg clk, reset;
	reg [23 : 0] data; //定义一个24位的寄存器用于存放待测数据码
	wire d, y;
	
	//生成测试信号逻辑
	initial
	begin
		clk = 0; reset = 1;
		#5  reset = 0;
		#20 reset = 1; 
        data = 24'b0011_1110_1001_1011_1010_0100;
		#(`halfperiod * 200) $stop; //执行100个时钟周期
	end
	
	//产生是时钟信号
	always #`halfperiod clk = ~clk;
	
	//用循环移位的方式待测数据码流(盘佛珠)
	always@(negedge clk) #5 data = {data[22 : 0], data[23]};
	assign d = data[23]; //将最高位输入到序列检测器中
	
	//实例化
	squence_detector m0(.clk(clk), .reset(reset), .d(d), .y(y));
	
endmodule

10010序列检测

/******************************p215 15.1 10010序列检测********************************/
module sequence_det(
              clk
            , rst
            , din
            , moore_o
            , mealy_o
);
 
input       clk     ;
input       rst     ;
input       din     ;
 
output reg  moore_o ;
output reg  mealy_o ;
 
reg [4:0]   state_c,state_n;
 
//=====================================================\
//****************   Define  Parameter   **************
//======================================================/
//状态编码:one-hot
localparam  Idle  = 5'b00001;
localparam  S1    = 5'b00010;
localparam  S10   = 5'b00100;
localparam  S100  = 5'b01000;//S10010可与之合并
localparam  S1001 = 5'b10000;
 
//状态转移
always@(posedge clk or posedge rst) begin
   if(rst)  state_c <= Idle;
   else     state_c <= state_n ;
end
 
//次态生成
always@(*)  begin
   if(rst)   state_n = Idle;
   else begin
        case(state_c)
            Idle   : state_n = din? S1    :Idle   ;
            S1     : state_n = din? S1    : S10    ;
            S10    : state_n = din? S1    : S100   ;
            S100   : state_n = din? S1001 : Idle   ;
            S1001  : state_n = din? S1    : S10010 ;
            default: state_n = Idle ;
        endcase
    end
end
 
/*Moore Machine
always@(posedge clk or posedge rst) begin
    if(rst) moore_o <= 1'b0;
    else    begin
        case(state_c)
            S10010:    moore_o <= 1'b1 ;
            default:   moore_o <= 1'b0 ;
        endcase
    end
end
*/

//Mealy Machine
always@(posedge clk or posedge rst) begin
    if(rst)     mealy_o <= 1'b0;
    else        mealy_o <= (state_c = S1001 && din ==0)?1'b1:1'b0;
   //输出不仅与state_c有关,还与当前的输入有关
end

endmodule

15.2 并行转特殊串行模块

top模块:M0+M1+M2
M0:产生时钟和data[3:0]数据输入信号,相当于测试模块;但是M1的输出ack信号是M0的输入,故又不只是测试模块,因此也要并入top中
M1:4bit并行数据转换为4位连续为一帧的的串行数据
M2:对4位连续的串行数据帧译码,输出16位并行译码信号
M1的通信协议
scl为不断输出的时钟信号;
如果scl为高:sda由1变0时刻,串行数据流开始,sda由0变1时刻,串行数据结束。
sda信号的串行数据位,必须在scl为低电平时变化。
在scl为高时,sda的变化用于指示数据流的开始或结束;在scl为低时,sda的变化才代表数据的传递。
夏宇闻复习笔记第15章:序列检测&并转串时序逻辑实例_第1张图片

M0模块:p2sda

/******************************p217 15.4 并转串+串行帧译码输出M1模块********************************/

module  p2sda(sclk,scl,data,sda,rst_n,d_ena);

input           sclk,rst_n,d_ena;
input   [3:0]   data;
reg     [3:0]   databuf;

output          scl,ack;
inout           sda;

reg             link_sda;
reg             sda_buf;
reg     [7:0]   state_c,state_n;

assign  sda = link_sda ? sda_buf : 1'bz;    //link_sda控制sda_buf输出到串行总线sda上,电平敏感

always@(posedge d_ena)  begin               //请求新数据时,在d_ena上升沿处将并行数据存入databuf;边沿敏感
    databuf <=  data;
end
    
parameter   READY   =   8'b0000_0000,
            START   =   8'b0000_0001,
            BIT1    =   8'b0000_0010,
            BIT2    =   8'b0000_0100,
            BIT3    =   8'b0000_1000,
            BIT4    =   8'b0001_0000,
            BIT5    =   8'b0010_0000,
            STOP    =   8'b0100_0000,
            IDLE    =   8'b1000_0000;      
            
//由并行输入时钟  二分频产生串行输出时钟scl,scl的周期为sclk的2倍
always@(posedge sclk or negedge rst_n)  begin
    if(!rst_n)  scl <=  1;
    else        scl <=  ~scl;   

//主状态机:产生控制信号,根据databuf中保存的数据,按照通信协议产生sda串行信号
//时序逻辑状态转移
always@(posedge sclk or negedge rst_n)  begin
    if(!rst_n)  state_c <=  READY;
    else        state_c <=  state_n;
end

//组合逻辑
always@(*)  begin       //我认为本不需要BIT5来提前把sda拉低,再去让sda产生结束高电平,我猜想是在查看仿真信号时有视觉上方便的地方,一个凸起的高脉冲便于查找
    case(state_c)
        READY:  state_n =  d_ena            ?   START   :   state_c;    //d_ena==1表示 并行数据databuf已到达,进入开始状态START
        START:  state_n =  (scl && d_ena)   ?   BIT1    :   state_c;    //scl为高 且 并行数据在传输,则sda开始串行数据流传输第一个bit,  即进入下一状态BIT1
        BIT1:   state_n =  !scl             ?   BIT2    :   state_c;    //scl为低时,送出串行数据最高位databuf[3](在输出always块操作),并进入下一状态BIT2
        BIT2:   state_n =  !scl             ?   BIT3    :   state_c;    //scl为低时,送出串行数据      databuf[2](在输出always块操作),并进入下一状态BIT3
        BIT3:   state_n =  !scl             ?   BIT4    :   state_c;    //scl为低时,送出串行数据      databuf[1](在输出always块操作),并进入下一状态BIT4 
        BIT4:   state_n =  !scl             ?   BIT5    :   state_c;    //scl为低时,送出串行数据最低位databuf[0](在输出always块操作),并进入下一状态BIT5 
        BIT5:   state_n =  !scl             ?   STOP    :   state_c;    //scl为低时,送出数据0,先把sda拉低,为产生sda高电平结束信号做仿真信号查看时的视觉准备(在输出always块操作),并进入下一状态STOP
        STOP:   state_n =   scl             ?   IDLE    :   state_c;    //scl为高 进入IDLE状态,不是READY状态,这个状态是为了将输出信号link_sda拉低,留出一拍的信号复位时间
        IDLE:   state_n =                       READY;                  //IDLE是一拍信号复位时间,在这个状态里把link_sda拉低
        default:state_n =                       8'bz;    
    endcase    
end

//时序逻辑输出  sda_buf:data的串行化输出信号/link_sda/ack:向M0请求信数据的信号
always@(negedge sclk or negedge rst_n)  begin           //sclk下降沿控制主状态机state;而scl上升沿在start_flag为1控制mstate
    if(!rst_n)  begin
        sda_buf <=  0;
        link_sda<=  0;
        ack     <=  0;
    end
    else  begin
        case(state_c)
            READY:   begin                  link_sda<=d_ena?1:0;    ack<=!d_ena?1:0;    end     //利用ack信号要求新的4位数据输入
            START:   begin  sdabuf  <=0;                                                end
            BIT1:    begin  sdabuf  <=(!scl)? databuf[3]:sdabuf;    ack<=0;             end
            BIT2:    begin  sdabuf  <=(!scl)? databuf[2]:sdabuf;                        end
            BIT3:    begin  sdabuf  <=(!scl)? databuf[1]:sdabuf;                        end
            BIT4:    begin  sdabuf  <=(!scl)? databuf[0]:sdabuf;                        end
            BIT5:    begin  sdabuf  <=(!scl)? 0         :sdabuf;                        end
            STOP:    begin  sdabuf  <=  scl ? 1         :sdabuf;                        end
            IDLE:    begin                  link_sda<=0;                                end
            default: begin  sdabuf  <=1;    link_sda<=0;                                end
        endcase
    end
end

endmodule

M1模块:out16hi

/******************************p217 15.5 并转串+串行帧译码输出M2模块********************************/
//按照协议接受串行数据源,对串行数据帧进行处理,并按照数据帧的值在相应位输出高电平
module  out16hi(scl,sda,outhigh); 
input           scl,sda;
output  [15:0]  outhigh;

reg     [4:0]   mstate_c,mstate_n;  //对串行数据的输入进行状态编码
reg     [3:0]   pdata,pdatabuf;     //记录串行数据输入的一帧4bit,串行数据sda->pdata->一帧传输结束后寄存至pdatabuf->译码为outhigh
reg             STARTFLAG,ENDFLAG;  //数据开始和结束标志位

//串行数据传输开始标志位(其实是传输进行标志位,传输全程STARTFLAG都是1)
always@(negedge sda)    begin               //检测到sda下降沿,则等待scl拉高,然后开始接收串行数据
    if(scl)             STARTFLAG   <=  1;  //串行数据开始标志位
    else if(ENDFLAG)    STARTFLAG   <=  0;  //若ENDFLAG==1,STARTFLAG归零
end

//串行数据传输结束标志位
always@(posedge sda)    begin               
    if(scl) begin                            //检测到sda上升沿,则等待scl拉高,然后结束接收串行数据,ENDFLAG=1,并将pdata传入寄存器??
        ENDFLAG <=  1;
        pdatabuf<=  pdata;
    end
    else    
        ENDFLAG <=  0;
end

parameter   MBIT0   =   5'b0_0001,
            MBIT1   =   5'b0_0010,
            MBIT2   =   5'b0_0100,
            MBIT3   =   5'b0_1000,
            MBIT4   =   5'b1_0000;

//主状态机,接收串行数据帧,并寄存在pdata
//时序逻辑-状态转移
always@(posedge scl)  begin
    mstate_c <=  mstate_n;
end
//组合逻辑-状态说明
always@(posedge scl)    begin
    if(STARTFLAG)
        case(mstate_c)
            MBIT0:  mstate_n    =   MBIT1;
            MBIT1:  mstate_n    =   MBIT2;
            MBIT2:  mstate_n    =   MBIT3;
            MBIT3:  mstate_n    =   MBIT4;
            MBIT4:  mstate_n    =   MBIT0;
            default:mstate_n    =   5'bz;
end
//时序逻辑-输出 pdata
always@(posedge scl)  begin
        case(mstate_c)
            MBIT0:  begin   pdata[3]    <=  sda;    $display("输出pdata[3]"); end
            MBIT1:  begin   pdata[2]    <=  sda;    $display("输出pdata[2]"); end
            MBIT2:  begin   pdata[1]    <=  sda;    $display("输出pdata[1]"); end
            MBIT3:  begin   pdata[0]    <=  sda;    $display("输出pdata[0]"); end
            MBIT4:  begin                           $display("暂停输出");     end
            default:begin                           $display("bug");          end
end


//将接收的串行数据帧译码为相应位的高电平 
always@(pdatabuf)   begin
    case(pdatabuf)
        4'b0001: outhigh = 16'b0000_0000_0000_0001;
        4'b0010: outhigh = 16'b0000_0000_0000_0010;
        4'b0011: outhigh = 16'b0000_0000_0000_0100;
        4'b0100: outhigh = 16'b0000_0000_0000_1000;
        4'b0101: outhigh = 16'b0000_0000_0001_0000;
        4'b0110: outhigh = 16'b0000_0000_0010_0000;
        4'b0111: outhigh = 16'b0000_0000_0100_0000;
        4'b1000: outhigh = 16'b0000_0000_1000_0000;
        4'b1001: outhigh = 16'b0000_0001_0000_0000;
        4'b1010: outhigh = 16'b0000_0010_0000_0000;
        4'b1011: outhigh = 16'b0000_0100_0000_0000;
        4'b1100: outhigh = 16'b0000_1000_0000_0000;
        4'b1101: outhigh = 16'b0001_0000_0000_0000;
        4'b1110: outhigh = 16'b0010_0000_0000_0000;
        4'b1111: outhigh = 16'b0100_0000_0000_0000;
        4'b0000: outhigh = 16'b1000_0000_0000_0000;
        default: outhigh = 16'bz;
    endcase
end

M2模块:sigdata

/******************************p222 15.6 并转串+串行帧译码输出M0模块********************************/
//类测试模块:数据初始化和生成,时钟模块生成
//当有ack==1信号来临,data[3:0]存入data_buf,data_buf通过state由高位到低位写入sda_buf
`timescale  1ns/1ns
`define     halfperiod  10

module  sigdata(ack,rst_n,data,ack,sclk);
input               ack;
output              rst_n,sclk;
        reg         rst_n,sclk;
output  reg [3:0]   data;

always  #(`halfperiod)  slck=~sclk;

initial begin
                        rst_n   =   0;
    #10                 rst_n   =   1;
    #(`halfperod*2+3)   rst_n   =   0;
 
end

//测试信号data输入4'b0000
initial begin
    sclk    =   0;
    data    =   0;
    #(`halfperod*100)   $stop;
end
//每收到一个ack正边沿信号,等候半个时钟加一丢丢然后将data+1
always@(posedge ack)    begin
    #(`halfperiod/2+3)  data=data+1;    
end

top模块

/******************************p223 15.7 并转串+串行帧译码输出top模块********************************/
`timescale  1ns/1ns
`include    "p2sda.v"
`include    "out16hi.v"
`include    "sigdata.v"

module  top;
reg         rst_n;
wire  [3:0] data;
wire  [15:0]outhigh; 
wire        sclk,scl,sda;
       
sigdata M0(.sclk(sclk),.data(data),.d_ena(e_ena));
p2sda   M1(.sclk(sclk),.d_ena(d_ena),.scl(scl),.sda(sda),.rst_n(rst_n),.data(data));
out16hi M2(.scl(scl),.sda(sda),.outhigh(outhigh));

你可能感兴趣的:(夏宇闻verilog)