risc-v中文社区

 找回密码
 立即注册
查看: 3888|回复: 4

[原创] 基于chisel/verilog的mini路由器实现之源码

  [复制链接]

347

主题

564

帖子

2237

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
2237
发表于 2021-9-28 09:24:33 | 显示全部楼层 |阅读模式
/**
  * @Author Joe_Liang
  * @Date 2021/9/28 10:19
  * @Version 1.0
  */
import chisel3._
import chisel3.util._
//总原则:
//主动发起方称为producer或类似AXI总线中的MASTER(数据有效valid和数据总线bits都是output,已准备好接收数据ready是input)
//数据接收方称为consumer或类似AXI总线中的SLAVE(已准备好接收数据ready为output,数据有效和数据总线bits为input)

//常量定义
object Router {
  val addressWidth    = 32 //32位地址宽度
  val dataWidth       = 64 //64位数据宽度
  val headerWidth     = 8 //封包头位宽
  val routeTableSize = 15 //路由表大小
  val numberOfOutputs = 4 //1输入4输出中的某一个
}
//读路由表(请求和响应)接口所用到的地址
class ReadCmd extends Bundle {
  val addr = UInt(Router.addressWidth.W) //地址位宽为Router.addressWidth
}
//写路由表(即加载路由表请求)接口所用到的地址和数据 地址继承自ReadCmd
class WriteCmd extends ReadCmd {
  val data = UInt(Router.dataWidth.W) //数据位宽为Router.dataWidth
}
//模块的输入(in)和输出(outs)用到的数据封包
class Packet extends Bundle {
  val header = UInt(Router.headerWidth.W) //包头
  val body = UInt(Router.dataWidth.W) //包体 数据位宽也是dataWidth
}

/**
  * 路由表IO,有四个ready-valid接口,一个Vec(n,EnqIO..)即多输出ready-valid接口
  * @param n 因为是1个输入接口对应多个输出接口中的某一个,n就代表有多少个输出接口
  */
class RouterIO(val n:Int) extends Bundle {
  //读路由表请求接口(SLAVE) 它的数据bits总线对应的是要读的地址即可,所以用ReadCmd
  val read_routing_table_request  = DeqIO(new ReadCmd)
  //读路由表响应接口(MASTER),它的数据bits总线对应的是路由地址,位宽为Router.addressWidth
  val read_routing_table_response = EnqIO(UInt(Router.addressWidth.W))
  //加载路由表请求接口(SLAVE)即用来写路由表的接口,它的数据bits总线需要提供模块内存MEM中对应的地址和数据
  val load_routing_table_request  = DeqIO(new WriteCmd) //bits总线需要提供对应MEM中的地址和数据,最终verilog中其实类似wire[31:0] data;wire[63:0] addr;
  //路由器工作时的输入接口(SLAVE),接收到的数据是封包类型Packet(包含包头和包体)
  val in  = DeqIO(new Packet)
  //路由器工作时的多个输出接口(MASTER),处理的数据类型当然也是封包Packet
  val outs = Vec(Router.numberOfOutputs,EnqIO(new Packet))
}
//模块
//routes packets by using their header as an index into an externally loaded and readable table,The number of addresses recognized does not need to match the number of outputs
class Router extends Module {
  val depth:Int = Router.routeTableSize //路由表的深度即路由表的大小
  val n:Int = Router.numberOfOutputs //路由器是1输入接口对应n输出接口中的某一路,n的取值就是来自numberOfOutputs
  val io = IO(new RouterIO(n)) //模块的IO接口定义
  //BigInt(n).bitLength表示:n输出这个n值需要多少bit位
  //开僻一段内存缓冲区,大小为depth,数据类型为UInt,宽度为上面bitLength所求bit位大小 为什么要这么做呢????
  val tbl = Mem(depth,UInt(BigInt(n).bitLength.W)) //

  //******start初始化输出信号 firrtl需要输出信号要有初始状态,否则就会出现:********
  //firrtl.passes.CheckInitialization$RefNotInitializedException:  : [module Router]  Reference io is not fully initialized.

  io.read_routing_table_request.nodeq() //读路由表请求接口(SLAVE)reaqdy置为false.B即中止传输
  io.load_routing_table_request.nodeq() //装载路由器表请求接口(SLAVE)中止传输
  io.read_routing_table_response.noenq() //读路由表响应接口(MASTER) noenq()表示valid发出数据无效指示,bits总线数据不关心
  io.read_routing_table_response.bits := 0.U  //虽然上一句nodeq()中bits总线不关心,此句指定设置为0
  io.in.nodeq() //模块输入接口(SLAVE)ready置为false.B表示中止接收传输
  //将N个输出接口(MASTER)的bits总线数据置为0,同时设置valid无效用以中止传输
  io.outs.foreach { out => //foreach循环处理每一个输出接口,包括初始化bits数据总线和valid
    out.bits := 0.U.asTypeOf(out.bits) //通过asTypeOf,将0值用来初始化数据总线 能否用out.bits := 0.U ?????????
    out.noenq() //每一个输出接口都将valid置为无效用来中止传输
  }
  //如果上面io.outs.foreach不用大括号而是用小括号(也可不用小括号,但函数体定义需要的大括号不能少),下面的代码格式则语法OK:
  //  io.outs.foreach((out)=>{
  //    out.bits := 0.U.asTypeOf(out.bits)
  //    out.nodeq()
  //  })
  //但如果是这样的语法格式:
  //  io.outs.foreach(out=>
  //    out.bits := 0.U.asTypeOf(out.bits)
  //    xxxxx 只要这里有语句代码则上面“:=”就会指示语法出错
  //  )
  //这种错误其实很好理解,因为foreach本来需要一个函数作为参数,上面既然没有用lambda形式的函数定义,根据scala语法只能是简写形式的函数定义,当然不能出现多条语句了

  //******end初始化输出信号********


  // We rely on Chisel's "last connect" semantics to override the default connections as appropriate.
  when(io.read_routing_table_request.valid && io.read_routing_table_response.ready) {//如果读路由表请求接口(SLAVE)的上游发来的valid数据有效和响应接口(MASTER)的下游发来的已准备好接收数据ready 这二个信号都有效
    io.read_routing_table_response.enq(//读路由表响应接口(MASTER)发出数据有效valid指示并同时也发出数据bits,此数据来自下面tbl中找到的值
      tbl( //以读路由表请求接口所返回的地址值作为索引,从tbl查找表中找到load_routing_table_request写进查找表里的值 这个值是什么意思???
        io.read_routing_table_request.deq().addr //读路由表请求接口(SLAVE)发出ready数据已准备好接收并接收数据(类型其实是ReadCmd),取其addr即地址
      ))
  }.elsewhen(io.load_routing_table_request.valid) {//如果加载路由表请求接口(SLAVE)收到数据有效valid指示 就要开始写路由表到tbl中
  val cmd = io.load_routing_table_request.deq() //加载路由表请求接口(SLAVE)发出ready接收数据,但要注意,此时bits数据总线返回的数据类型是WriteCmd
    tbl(cmd.addr) := cmd.data //tbl查找表中对应地址addr内存存储的值是data
    //    println() //是打印在控制台
    //    printf() //在模拟器中打印(比如当某个条件满足时打印)
    printf("setting tbl(%d) to %d\n",cmd.addr,cmd.data)
  }.elsewhen(io.in.valid) { //如果模块的输入接口(SLAVE)收到上游发来的数据(其实是Packet)有效valid
    val pkt = io.in.bits //bits总线实际数据类型是Packet,包含包头header和包体body
  //log2Ceil(Router.routeTableSize):(Router.routeTableSize-1)这个值需要的bit位
  val idx = tbl(pkt.header(log2Ceil(Router.routeTableSize),0)) //从封包头取0~(路由表深度大小-1)所需bit位对应的值作为tbl的索引取值 其实理论上应该取pkt.header(31,0),但肯定不需要取这么大值,按实际取即可
    //也就是说封包中头header其实保存的是tbl中的地址索引,根据这个索引取得tbl中的值表示的是输出接口序号
    when(io.outs(idx).ready) { //输出接口(MASTER)收到下游发来的已准备好接收数据ready信号
      io.in.deq() //发出ready指示输入接口收到的数据OK, 也就是说此时pkt中收到的io.in.bits是真正的有效的数据了
      io.outs(idx).enq(pkt) //输出接口调用enq(pkt)表示发出数据有效valid指示并将数据(其实是通过组合逻辑得到的输入接口中的封包数据)也输出
      printf("got packet to route header %d, data %d, being routed to out(%d)\n",pkt.header,pkt.body,tbl(pkt.header))
    }
  }
}



回复

使用道具 举报

347

主题

564

帖子

2237

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
2237
 楼主| 发表于 2021-9-28 09:29:28 | 显示全部楼层
对应verilog代码如下:
module Router(
  input         clock,
  input         reset,
  output        io_read_routing_table_request_ready,
  input         io_read_routing_table_request_valid,
  input  [31:0] io_read_routing_table_request_bits_addr,
  input         io_read_routing_table_response_ready,
  output        io_read_routing_table_response_valid,
  output [31:0] io_read_routing_table_response_bits,
  output        io_load_routing_table_request_ready,
  input         io_load_routing_table_request_valid,
  input  [31:0] io_load_routing_table_request_bits_addr,
  input  [63:0] io_load_routing_table_request_bits_data,
  output        io_in_ready,
  input         io_in_valid,
  input  [7:0]  io_in_bits_header,
  input  [63:0] io_in_bits_body,
  input         io_outs_0_ready,
  output        io_outs_0_valid,
  output [7:0]  io_outs_0_bits_header,
  output [63:0] io_outs_0_bits_body,
  input         io_outs_1_ready,
  output        io_outs_1_valid,
  output [7:0]  io_outs_1_bits_header,
  output [63:0] io_outs_1_bits_body,
  input         io_outs_2_ready,
  output        io_outs_2_valid,
  output [7:0]  io_outs_2_bits_header,
  output [63:0] io_outs_2_bits_body,
  input         io_outs_3_ready,
  output        io_outs_3_valid,
  output [7:0]  io_outs_3_bits_header,
  output [63:0] io_outs_3_bits_body
);
  reg [2:0] tbl [0:14]; // @[Router.scala 55:16]
  reg [31:0] _RAND_0;
  wire [2:0] tbl__T_6_data; // @[Router.scala 55:16]
  wire [3:0] tbl__T_6_addr; // @[Router.scala 55:16]
  reg [31:0] _RAND_1;
  wire [2:0] tbl__T_13_data; // @[Router.scala 55:16]
  wire [3:0] tbl__T_13_addr; // @[Router.scala 55:16]
  reg [31:0] _RAND_2;
  wire [2:0] tbl__T_17_data; // @[Router.scala 55:16]
  wire [3:0] tbl__T_17_addr; // @[Router.scala 55:16]
  reg [31:0] _RAND_3;
  wire [2:0] tbl__T_8_data; // @[Router.scala 55:16]
  wire [3:0] tbl__T_8_addr; // @[Router.scala 55:16]
  wire  tbl__T_8_mask; // @[Router.scala 55:16]
  wire  tbl__T_8_en; // @[Router.scala 55:16]
  wire  _T_4 = io_read_routing_table_request_valid & io_read_routing_table_response_ready; // @[Router.scala 86:44]
  wire  _T_10 = ~reset; // @[Router.scala 96:11]
  wire  _GEN_4 = 2'h1 == tbl__T_13_data[1:0] ? io_outs_1_ready : io_outs_0_ready; // @[Router.scala 102:30]
  wire  _GEN_8 = 2'h2 == tbl__T_13_data[1:0] ? io_outs_2_ready : _GEN_4; // @[Router.scala 102:30]
  wire  _GEN_12 = 2'h3 == tbl__T_13_data[1:0] ? io_outs_3_ready : _GEN_8; // @[Router.scala 102:30]
  wire  _GEN_16 = 2'h0 == tbl__T_13_data[1:0]; // @[Decoupled.scala 47:20]
  wire  _GEN_17 = 2'h1 == tbl__T_13_data[1:0]; // @[Decoupled.scala 47:20]
  wire  _GEN_18 = 2'h2 == tbl__T_13_data[1:0]; // @[Decoupled.scala 47:20]
  wire  _GEN_19 = 2'h3 == tbl__T_13_data[1:0]; // @[Decoupled.scala 47:20]
  wire  _GEN_29 = _GEN_12 & _GEN_16; // @[Router.scala 102:30]
  wire  _GEN_30 = _GEN_12 & _GEN_17; // @[Router.scala 102:30]
  wire  _GEN_31 = _GEN_12 & _GEN_18; // @[Router.scala 102:30]
  wire  _GEN_32 = _GEN_12 & _GEN_19; // @[Router.scala 102:30]
  wire  _GEN_46 = io_in_valid & _GEN_12; // @[Router.scala 97:27]
  wire  _GEN_47 = io_in_valid & _GEN_29; // @[Router.scala 97:27]
  wire  _GEN_48 = io_in_valid & _GEN_30; // @[Router.scala 97:27]
  wire  _GEN_49 = io_in_valid & _GEN_31; // @[Router.scala 97:27]
  wire  _GEN_50 = io_in_valid & _GEN_32; // @[Router.scala 97:27]
  wire  _GEN_68 = io_load_routing_table_request_valid ? 1'h0 : io_in_valid; // @[Router.scala 91:51]
  wire  _GEN_69 = io_load_routing_table_request_valid ? 1'h0 : _GEN_46; // @[Router.scala 91:51]
  wire  _GEN_70 = io_load_routing_table_request_valid ? 1'h0 : _GEN_47; // @[Router.scala 91:51]
  wire  _GEN_71 = io_load_routing_table_request_valid ? 1'h0 : _GEN_48; // @[Router.scala 91:51]
  wire  _GEN_72 = io_load_routing_table_request_valid ? 1'h0 : _GEN_49; // @[Router.scala 91:51]
  wire  _GEN_73 = io_load_routing_table_request_valid ? 1'h0 : _GEN_50; // @[Router.scala 91:51]
  wire [2:0] _GEN_87 = _T_4 ? tbl__T_6_data : 3'h0; // @[Router.scala 86:85]
  wire  _GEN_112 = ~_T_4; // @[Router.scala 96:11]
  wire  _GEN_113 = _GEN_112 & io_load_routing_table_request_valid; // @[Router.scala 96:11]
  wire  _GEN_115 = ~io_load_routing_table_request_valid; // @[Router.scala 105:13]
  wire  _GEN_116 = _GEN_112 & _GEN_115; // @[Router.scala 105:13]
  wire  _GEN_117 = _GEN_116 & io_in_valid; // @[Router.scala 105:13]
  wire  _GEN_118 = _GEN_117 & _GEN_12; // @[Router.scala 105:13]
  assign tbl__T_6_addr = io_read_routing_table_request_bits_addr[3:0];
  `ifndef RANDOMIZE_GARBAGE_ASSIGN
  assign tbl__T_6_data = tbl[tbl__T_6_addr]; // @[Router.scala 55:16]
  `else
  assign tbl__T_6_data = tbl__T_6_addr >= 4'hf ? _RAND_1[2:0] : tbl[tbl__T_6_addr]; // @[Router.scala 55:16]
  `endif // RANDOMIZE_GARBAGE_ASSIGN
  assign tbl__T_13_addr = io_in_bits_header[3:0];
  `ifndef RANDOMIZE_GARBAGE_ASSIGN
  assign tbl__T_13_data = tbl[tbl__T_13_addr]; // @[Router.scala 55:16]
  `else
  assign tbl__T_13_data = tbl__T_13_addr >= 4'hf ? _RAND_2[2:0] : tbl[tbl__T_13_addr]; // @[Router.scala 55:16]
  `endif // RANDOMIZE_GARBAGE_ASSIGN
  assign tbl__T_17_addr = io_in_bits_header[3:0];
  `ifndef RANDOMIZE_GARBAGE_ASSIGN
  assign tbl__T_17_data = tbl[tbl__T_17_addr]; // @[Router.scala 55:16]
  `else
  assign tbl__T_17_data = tbl__T_17_addr >= 4'hf ? _RAND_3[2:0] : tbl[tbl__T_17_addr]; // @[Router.scala 55:16]
  `endif // RANDOMIZE_GARBAGE_ASSIGN
  assign tbl__T_8_data = io_load_routing_table_request_bits_data[2:0];
  assign tbl__T_8_addr = io_load_routing_table_request_bits_addr[3:0];
  assign tbl__T_8_mask = 1'h1;
  assign tbl__T_8_en = _T_4 ? 1'h0 : io_load_routing_table_request_valid;
  assign io_read_routing_table_request_ready = io_read_routing_table_request_valid & io_read_routing_table_response_ready; // @[Decoupled.scala 72:20 Decoupled.scala 65:20]
  assign io_read_routing_table_response_valid = io_read_routing_table_request_valid & io_read_routing_table_response_ready; // @[Decoupled.scala 56:20 Decoupled.scala 47:20]
  assign io_read_routing_table_response_bits = {{29'd0}, _GEN_87}; // @[Router.scala 63:39 Decoupled.scala 48:19]
  assign io_load_routing_table_request_ready = _T_4 ? 1'h0 : io_load_routing_table_request_valid; // @[Decoupled.scala 72:20 Decoupled.scala 65:20]
  assign io_in_ready = _T_4 ? 1'h0 : _GEN_69; // @[Decoupled.scala 72:20 Decoupled.scala 65:20]
  assign io_outs_0_valid = _T_4 ? 1'h0 : _GEN_70; // @[Decoupled.scala 56:20 Decoupled.scala 47:20]
  assign io_outs_0_bits_header = io_in_bits_header; // @[Router.scala 67:14 Decoupled.scala 48:19]
  assign io_outs_0_bits_body = io_in_bits_body; // @[Router.scala 67:14 Decoupled.scala 48:19]
  assign io_outs_1_valid = _T_4 ? 1'h0 : _GEN_71; // @[Decoupled.scala 56:20 Decoupled.scala 47:20]
  assign io_outs_1_bits_header = io_in_bits_header; // @[Router.scala 67:14 Decoupled.scala 48:19]
  assign io_outs_1_bits_body = io_in_bits_body; // @[Router.scala 67:14 Decoupled.scala 48:19]
  assign io_outs_2_valid = _T_4 ? 1'h0 : _GEN_72; // @[Decoupled.scala 56:20 Decoupled.scala 47:20]
  assign io_outs_2_bits_header = io_in_bits_header; // @[Router.scala 67:14 Decoupled.scala 48:19]
  assign io_outs_2_bits_body = io_in_bits_body; // @[Router.scala 67:14 Decoupled.scala 48:19]
  assign io_outs_3_valid = _T_4 ? 1'h0 : _GEN_73; // @[Decoupled.scala 56:20 Decoupled.scala 47:20]
  assign io_outs_3_bits_header = io_in_bits_header; // @[Router.scala 67:14 Decoupled.scala 48:19]
  assign io_outs_3_bits_body = io_in_bits_body; // @[Router.scala 67:14 Decoupled.scala 48:19]
`ifdef RANDOMIZE_GARBAGE_ASSIGN
`define RANDOMIZE
`endif
`ifdef RANDOMIZE_INVALID_ASSIGN
`define RANDOMIZE
`endif
`ifdef RANDOMIZE_REG_INIT
`define RANDOMIZE
`endif
`ifdef RANDOMIZE_MEM_INIT
`define RANDOMIZE
`endif
`ifndef RANDOM
`define RANDOM $random
`endif
`ifdef RANDOMIZE_MEM_INIT
  integer initvar;
`endif
`ifndef SYNTHESIS
initial begin
  `ifdef RANDOMIZE
    `ifdef INIT_RANDOM
      `INIT_RANDOM
    `endif
    `ifndef VERILATOR
      `ifdef RANDOMIZE_DELAY
        #`RANDOMIZE_DELAY begin end
      `else
        #0.002 begin end
      `endif
    `endif
  _RAND_0 = {1{`RANDOM}};
  `ifdef RANDOMIZE_MEM_INIT
  for (initvar = 0; initvar < 15; initvar = initvar+1)
    tbl[initvar] = _RAND_0[2:0];
  `endif // RANDOMIZE_MEM_INIT
  _RAND_1 = {1{`RANDOM}};
  _RAND_2 = {1{`RANDOM}};
  _RAND_3 = {1{`RANDOM}};
  `endif // RANDOMIZE
end // initial
`endif // SYNTHESIS
  always @(posedge clock) begin
    if(tbl__T_8_en & tbl__T_8_mask) begin
      tbl[tbl__T_8_addr] <= tbl__T_8_data; // @[Router.scala 55:16]
    end
    `ifndef SYNTHESIS
    `ifdef PRINTF_COND
      if (`PRINTF_COND) begin
    `endif
        if (_GEN_113 & _T_10) begin
          $fwrite(32'h80000002,"setting tbl(%d) to %d\n",io_load_routing_table_request_bits_addr,io_load_routing_table_request_bits_data); // @[Router.scala 96:11]
        end
    `ifdef PRINTF_COND
      end
    `endif
    `endif // SYNTHESIS
    `ifndef SYNTHESIS
    `ifdef PRINTF_COND
      if (`PRINTF_COND) begin
    `endif
        if (_GEN_118 & _T_10) begin
          $fwrite(32'h80000002,"got packet to route header %d, data %d, being routed to out(%d)\n",io_in_bits_header,io_in_bits_body,tbl__T_17_data); // @[Router.scala 105:13]
        end
    `ifdef PRINTF_COND
      end
    `endif
    `endif // SYNTHESIS
  end
endmodule
回复

使用道具 举报

347

主题

564

帖子

2237

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
2237
 楼主| 发表于 2021-9-28 09:30:25 | 显示全部楼层
期待仿真测试及相关源代码。。。。。。
回复

使用道具 举报

347

主题

564

帖子

2237

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
2237
 楼主| 发表于 2021-9-28 17:04:28 | 显示全部楼层
有朋友问我为什么上面foreach代码要那样写,还说了很多错误的想法,我有感与此,特分析如下:
如果上面io.outs.foreach不用大括号而是用小括号(也可不用小括号,但函数体定义需要的大括号不能少),下面的代码格式则语法OK:
  io.outs.foreach((out)=>{
    out.bits := 0.U.asTypeOf(out.bits)
    out.nodeq()
  })
但如果是这样的语法格式:
  io.outs.foreach(out=>
    out.bits := 0.U.asTypeOf(out.bits)
    xxxxx 只要这里有语句代码则上面“:=”就会指示语法出错
  )
这种错误其实很好理解,因为foreach本来需要一个函数作为参数,上面既然没有用lambda形式的函数定义,根据scala语法只能是简写形式的函数定义,当然不能出现多条语句了。
回复

使用道具 举报

347

主题

564

帖子

2237

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
2237
 楼主| 发表于 2021-9-30 10:02:57 | 显示全部楼层
图示数据结构逻辑关系:


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



Archiver|手机版|小黑屋|risc-v中文社区

GMT+8, 2024-5-2 06:32 , Processed in 0.030359 second(s), 18 queries .

risc-v中文社区论坛 官方网站

Copyright © 2018-2021, risc-v open source

快速回复 返回顶部 返回列表