路科v0,第五周,练习代码

路科v0实验,这是第五周练习完成代码,

学习内容如下:
定义了接口
使用模块定义了数据发生器gen(),激励器,监视器。
尚未使用类来完成监视器的设计

//`timescale 1ns/10ps

typedef struct{//定义一个结构体变量,用于存放数据信息
  bit[3:0] src;//输入数据(din)的通道
  bit[3:0] dst;//输出数据(dout)的通道
  bit[7:0] data[];//发送的数据
} rt_packet_t;


interface rt_io;
  logic clk;
  logic rstn;
  logic[15:0] din; 
  logic[15:0] frame_n;
  logic[15:0] valid_n;
  logic[15:0] dout;
  logic[15:0] valido_n;
  logic[15:0] busy_n;
  logic[15:0] frameo_n;
endinterface



//用于发生激励
module rt_stimulator(
  rt_io io
);
  
  rt_packet_t pkts[$];//激励模块的队列
  int src_chal_status[int];

  function void put_pkt(input rt_packet_t p);//激励模块的队列中加入一个数据
    pkts.push_back(p);
  endfunction


  task drive_reset_proc();
    @(negedge io.rstn);//检测到复位信号后,产生一个复位动作
    io.din <= 0;
    io.frame_n <= '1;
    io.valid_n <= '1;
  endtask

  //saddr-in,daddr-out,
  task automatic drive_chal_proc(bit[3:0] saddr,bit[3:0] daddr,byte unsigned data[]);
    $display("src_chal[%0d], chal[%0d] started",saddr,daddr);
    $display("data = %h",data);
    for(int i = 0;i<4;i++) begin//发送四位的daddr(dst),即那个数据通道
      @(posedge io.clk);
      io.din[saddr] <= daddr[i];
      io.valid_n[saddr] <= 1'b0;//暂时先不开启输入?
      io.frame_n[saddr] <= 1'b0;
    end

    //drive pad phase
    for(int i = 0;i<5;i++) begin//按照时序,发送五个空拍
      @(posedge io.clk);
      io.din[saddr] <= 1'b1;
      io.valid_n[saddr] <= 1'b0;
      io.frame_n[saddr] <= 1'b0;
    end

    //drive data phase
    foreach(data[id]) begin//每个data为一个字节,每个结构体里的data可能有多个字节的数据
      for(int i = 0;i<8;i++) begin//开始发送一个字节
        @(posedge io.clk);
        io.din[saddr] <= data[id][i];
        io.valid_n[saddr] <= 1'b0;
        if(id == data.size()-1 && i == 7)
          io.frame_n[saddr] <= 1'b1;//数据发送完毕
        else
          io.frame_n[saddr] <= 1'b0;//else继续发送
      end
    end
    
    @(posedge io.clk);//结束动作
    io.din[0] <= 1'b0;
    io.valid_n[saddr] <= 1'b1;
    io.frame_n[saddr] <= 1'b1;
    $display("src_chal[%0d], chal[%0d] finished",saddr,daddr);
  endtask


  //等待src(输入通道)可用(-1),src_chal_status是一个关联数组,存放了src是否被占用的状态
  task automatic wait_src_chanl_avail(rt_packet_t p);
    if(!src_chal_status.exists(p.src))
      src_chal_status[p.src] = p.dst;
    else if(src_chal_status[p.src] >= 0)
      wait(src_chal_status[p.src] == -1);
  endtask


  //将某一输入通道置为空闲
  function automatic set_src_chanl_avail(rt_packet_t p);
    src_chal_status[p.src] = -1;
  endfunction


  initial begin
    drive_reset_proc();//这个函数的作用:检测到rstn下降沿后进行一个复位动作
  end


  initial begin
    @(negedge io.rstn);//检测到复位信号(rstn的下降沿)
    repeat(10) @(posedge io.clk);//等待十个复位信号
    forever begin
      automatic rt_packet_t p;//声明一个静态结构体
      wait(pkts.size()>0);//若队列为空则一直等待,直到队列中存在数据
      p = pkts.pop_front();//从队列中取出数据
      wait_src_chanl_avail(p);//p中有p.src,src指示了输入通道,这个方法会一直等待这个通道为可用状态,并占用他。
      fork //出发线程,但不等待他执行完成(fork-join_none)
        begin
          drive_chal_proc(p.src,p.dst,p.data);//发送数据
          set_src_chanl_avail(p);//发送完成后,将占用的通道置为空闲状态
        end
      join_none
    end
    
  end



endmodule




module rt_monitor(rt_io io);//定义监视器模块
  //注 rt_packet_t是前面定义的结构体,用于储存数据信息,包括dut的输入通道信息,输出通道信息,以及data
  rt_packet_t in_pkts[16][$];//定义16个队列,每个队列用于存放一个结构体信息
  rt_packet_t out_pkts[16][$];

  task automatic moni_chal_in(bit[3:0] id);//定义输入数据的monitor
    rt_packet_t pkt;//一个结构体,用于储存一次检测的信息
    pkt.src = id;//id是监测的通道id。即pkt.src(输入通道)
    forever begin
      pkt.data.delete();//清空这个给结构体
      @(negedge io.frame_n[id]);//检测frame_n下降沿,数据开始发送
      $display("[monitor-in]%d start",id);
      for(int i=0;i<4;i++) begin
        @(posedge io.clk);//每个时钟上升沿接收1bit数据,按照时序,现在接受的是四位的地址数据(这个地址数据其实是dout的输出通道数)
        pkt.dst[i] = io.din[id];
      end
      repeat(5) @(negedge io.clk);//按照时序,这里有五个空的时钟,不关心他的值
      do
        begin
          pkt.data = new[pkt.data.size+1](pkt.data);//这里没明白,可能是分配新的内存空间
          for(int i=0;i<8;i++) begin
            @(posedge io.clk);//八个时钟上升沿,接收一个字节的data
            pkt.data[pkt.data.size-1][i] = io.din[id];
          end
        end
      while(!io.frame_n[id]);//一直重复上面这个过程(接受一字节的data),直到io.frame_n置1,因为按照时序,数据发送结束后,io.frame_n会拉高
      in_pkts[id].push_back(pkt);
      $display("[monitor-in]%d train finished src = %d, dst = %d, data = %h ",id,pkt.src, pkt.dst, pkt.data);
    end
  endtask

  task automatic moni_chal_out(bit[3:0] id);
    rt_packet_t pkt;
    forever begin
      pkt.data.delete();//清空数据
      pkt.src = 0;//dut的输出中,无从得知一个数据的src是哪来的,这里直接定义为0
      pkt.dst = id;//监测的通道id
      @(negedge io.frameo_n[id]);//io.frameo_n[id]下降沿表明dut要开始输出数据了(参见时序图)
      $display("[monitor-out]%d start",id);
      do
        begin
          pkt.data = new[pkt.data.size+1](pkt.data);
          for(int i=0;i<8;i++) begin//dut的输出仅输出data,
            @(posedge io.clk iff !io.valido_n[id]);//dut输出时会将valido拉低,因此这里的判断是:时钟上升沿 并且 valido为低电平
            pkt.data[pkt.data.size-1][i] = io.dout[id];
          end
        end
      while(io.frameo_n[id] == 1);//这里与din的监测逻辑一样,dut的dout输出结束后,会将frameo_n拉高
      out_pkts[id].push_back(pkt);
      $display("[monitor-out]%d train finished src = %d, dst = %d, data = %h ",id,pkt.src, pkt.dst, pkt.data);
    end
  endtask

  initial begin:moni_chal_in_proc
    foreach(in_pkts[i]) begin//in_pkts是16个队列,对应每一个通道,这里对每一个通道都进行监测
      automatic int chanl_in_id = i;
      fork//出发监测线程,(且不等待他结束)
          moni_chal_in(chanl_in_id);//这一个task里面有forever,一直对dut进行监测
      join_none
    end
  end

  initial begin:moni_chal_out_proc
    foreach(out_pkts[i]) begin
      automatic int chanl_out_id = i;
      fork
        moni_chal_out(chanl_out_id);
      join_none
    end
  end

endmodule



//用于生成数据的模块
module rt_generator;
  rt_packet_t pkts[$];

  function void put_pkt(input rt_packet_t p);
    pkts.push_back(p);//将一条数据放入队列中
  endfunction

  task get_pkt(output rt_packet_t p);
    wait(pkts.size()>0)//等待直到pkts队列里部位空
      p = pkts.pop_front();//取出一条数据
  endtask

  function void gen_pkt(int src = -1, int dst = -1);
  endfunction

endmodule



module top;

endmodule



module tb1;//这里就相当于一个顶层设计了
  var bit clk,rstn;

  //generate clk
  initial begin
    forever #5 clk = ~clk;
  end

  //generate rstn
  initial begin
    #5 rstn <= 1;
    #10 rstn <= 0;
    #10 rstn <= 1;
  end

  rt_io io();//例化一个接口
  assign io.clk = clk;
  assign io.rstn = rstn;


  router dut(
  .reset_n(io.rstn), 
  .clock(io.clk),
  .frame_n(io.frame_n),
  .valid_n(io.valid_n),
  .din(io.din),
  .dout(io.dout),
  .busy_n(io.busy_n),
  .valido_n(io.valido_n),
  .frameo_n(io.frameo_n)
  );

  rt_monitor monitor(io);//例化一个检测器,并连接接口:io

  rt_stimulator stim(io);//例化一个激励器

  rt_generator gen();//例化一个数据生成模块


  initial begin:generate_proc//生成数据
    rt_packet_t p;
    p = '{0, 3, '{8'ha1,8'hf0}};
    gen.put_pkt(p);
    gen.put_pkt('{1, 3, '{8'haa,8'hff}});
    gen.put_pkt('{1, 2, '{8'h0f,8'h0f}});
    gen.put_pkt('{0, 3, '{8'h0f,8'h0f,8'haa}});
  end

  initial begin:transmit_proc
    rt_packet_t p;//一个结构体句柄
    forever begin
      gen.get_pkt(p);//取出一个数据
      stim.put_pkt(p);//将该数据送入激励发生器,即发送数据
    end
  end

endmodule
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值