tim-sof
tim-sof

Reputation: 21

systemverilog union type in chisel

I rewritting some systermverilog code into chisel, and I cannot find a good way to express the systemverilog's packed union in chisel. For example,

typedef struct packed {
    logic [3:0]     version;
    logic [3:0]     ihl;
    logic [7:0]     tos;
    logic [15:0]    total_length;
    logic [15:0]    id;
    logic           reserved;
    logic           df;
    logic           mf;
    logic [12:0]    frag_offset;
    logic [7:0]     ttl;
    logic [7:0]     protocol;
    logic [15:0]    checksum;
    logic [31:0]    ip_sa;
    logic [31:0]    ip_da;
} ipv4_s;

typedef struct packed {
    logic [63:0] DW0;
    logic [63:0] DW1;
    logic [31:0] DW2;
} common_20b_s;

typedef union packed {
    ipv4_s  structured;
    common_20b_s array;
    logic [$bits(ipv4_s)-1:0] bits;
} ipv4_hdr_u;

module test (
    input clk,
    input [$bits(ipv4_s)-1:0] pkt_stream_in,
    input [3:0] mode,
    input [127:0] config_in,
    output reg [$bits(ipv4_s)-1:0] pkt_stream_out
);

    ipv4_hdr_u ipv4_hdr;
    always @* begin
        ipv4_hdr.bits = pkt_stream_in;

        case(mode)
            0: ipv4_hdr.structured.ip_sa = config_in[31:0];
            1: ipv4_hdr.array.DW0 = config_in[63:0];
        endcase
    end

    always @(posedge clk)
        pkt_stream_out <= ipv4_hdr.bits;

endmodule: test

For those two data structure ipv4_s and common_2b_s, each field in the structure can be updated individually, but to chisel. Is it any way to express this kind of function?

Upvotes: 2

Views: 346

Answers (1)

Jack Koenig
Jack Koenig

Reputation: 6065

There's no built-in support for packed unions, but you can create custom classes (subtyping Bundle) and build something similar yourself. It leads to a little more boiler plate so I think some built-in support would be nice, but it gets the job done:

Runnable transliteration: https://scastie.scala-lang.org/5jOMpEYuRbu9VbzScvDffQ

Pasted below:

import chisel3._
import chisel3.util._
import chisel3.stage.ChiselStage

class IPV4_s extends Bundle {
  val version = UInt(4.W)
  val ihl = UInt(4.W)
  val tos = UInt(8.W)
  val total_length = UInt(16.W)
  val id = UInt(16.W)
  val reserved = Bool()
  val df = Bool()
  val mf = Bool()
  val frag_offset = UInt(13.W)
  val ttl = UInt(8.W)
  val protocol = UInt(8.W)
  val checksum = UInt(16.W)
  val ip_sa = UInt(32.W)
  val ip_da = UInt(32.W)
}
object IPV4_s {
  // Utility, I got sick of typing this righthand expression
  val width = (new IPV4_s).getWidth
}

class Common_20b_s extends Bundle {
  val DW0 = UInt(64.W)
  val DW1 = UInt(64.W)
  val DW2 = UInt(32.W)
}


class IPV4_hdr_u extends Bundle {
  val bits = UInt(IPV4_s.width.W)
  // These helpers aren't really necessary but can save some typing
  def asIPV4_s = this.asTypeOf(new IPV4_s)
  def asCommon_20b_s = this.asTypeOf(new Common_20b_s)
}

class Test extends MultiIOModule {
  // clock is inherited
  val pkt_stream_in = IO(Input(new IPV4_hdr_u))
  val mode = IO(Input(UInt(4.W)))
  val config_in = IO(Input(UInt(128.W)))
  val pkt_stream_out = IO(Output(new IPV4_hdr_u))

  // Default to using stream_in
  val ipv4_hdr = WireInit(pkt_stream_in)
  switch (mode) {
    is (0.U) {
      // Cast input and use temporary for subfield assignment
      val t = WireInit(pkt_stream_in.asIPV4_s)
      t.ip_sa := config_in(31, 0)
      ipv4_hdr := t.asTypeOf(new IPV4_hdr_u)
    }
    is (1.U) {
      val t = WireInit(pkt_stream_in.asCommon_20b_s)
      t.DW0 := config_in(63, 0)
      ipv4_hdr := t.asTypeOf(new IPV4_hdr_u)
    }
  }
  pkt_stream_out := ipv4_hdr
}

Upvotes: 2

Related Questions