Home CONNECT Note (7) - So... why Chisel?
Post
Cancel

CONNECT Note (7) - So... why Chisel?

We define our AXI4 protocol interface as follows.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
trait AXI4Id extends Bundle with AXI4Parameters {
  val id = UInt(AXI4IdWidth.W)
}

class AXI4LiteChannelA extends Bundle with AXI4Parameters {
  val addr = UInt(AXI4AddrWidth.W)
  val prot = UInt(3.W)
}

class AXI4ChannelA extends AXI4LiteChannelA with AXI4Id {
  val len    = UInt(8.W)
  val size   = UInt(3.W)
  val burst  = UInt(2.W)
  val lock   = Bool()
  val cache  = UInt(4.W)
  val qos    = UInt(4.W)
  val region = UInt(4.W)
}

// ...

class AXI4LiteIO extends Bundle {
  val aw = Decoupled(new AXI4LiteChannelA)
  val w  = Decoupled(new AXI4LiteChannelW)
  val b  = Flipped(Decoupled(new AXI4LiteChannelB))
  val ar = Decoupled(new AXI4LiteChannelA)
  val r  = Flipped(Decoupled(new AXI4LiteChannelR))
}

class AXI4IO extends AXI4LiteIO {
  override val aw = Decoupled(new AXI4ChannelA)
  override val w  = Decoupled(new AXI4ChannelW)
  override val b  = Flipped(Decoupled(new AXI4ChannelB))
  override val ar = Decoupled(new AXI4ChannelA)
  override val r  = Flipped(Decoupled(new AXI4ChannelR))
}

OOP features including templates, inheritance and polymorphism make things much easier for RTL programmers. For example, in terms of AXI4 bridge design, almost every line of code can be reused for AXI4-Lite protocol. The following code snippet shows one of the FSMs in the bridge.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// FSM to handle AXI master device read
switch(r_state) {
  is(r_addr) {
    when(io.axi.ar.fire) {
      r_state := r_data
    }
  }
  is(r_data) {
    if (io.axi.r.bits.getClass == classOf[AXI4ChannelR]) {
      when(io.axi.r.fire && io.axi.r.bits.asInstanceOf[AXI4ChannelR].last) {
        r_state := r_addr
      }
    } else {
      when(io.axi.r.fire) {
        r_state := r_addr
      }
    }
  }
}

We also need to redefine the method to pack and unpack the data as follows.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Pack the data in aw/ar channel
object AXI4ChannelA2PacketData {
  def apply[C <: AXI4LiteChannelA](a: C, is_w: Bool): UInt = {
    if (a.getClass == classOf[AXI4ChannelA]) {
      val a_ = a.asInstanceOf[AXI4ChannelA]
      Cat(
        a_.id,
        a_.addr,
        a_.region,
        a_.qos,
        a_.prot,
        a_.cache,
        a_.lock.asUInt,
        a_.burst,
        a_.size,
        a_.len,
        Mux(is_w, AXI4ChannelID.AW, AXI4ChannelID.AR)
      )
    } else {
      Cat(
        a.addr,
        a.prot,
        Mux(is_w, AXI4ChannelID.AW, AXI4ChannelID.AR)
      )
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Unpack the data in aw/ar channel
object Packet2AXI4ChannelA {
  def apply[B <: AXI4LiteIO](bus_io: B)(packet: UInt): AXI4LiteChannelA = {
    assert(packet.getWidth == AXI4PacketWidth(bus_io))
    if (bus_io.getClass == classOf[AXI4IO]) {
      val a = Wire(new AXI4ChannelA)
      a.id := packet(
        31 + AXI4Parameters.AXI4AddrWidth + AXI4Parameters.AXI4IdWidth,
        32 + AXI4Parameters.AXI4AddrWidth
      )
      a.addr   := packet(31 + AXI4Parameters.AXI4AddrWidth, 32)
      a.region := packet(31, 28)
      a.qos    := packet(27, 24)
      a.prot   := packet(23, 21)
      a.cache  := packet(20, 17)
      a.lock   := packet(16).asBool
      a.burst  := packet(15, 14)
      a.size   := packet(13, 11)
      a.len    := packet(10, 3)
      a
    } else {
      val a = Wire(new AXI4LiteChannelA)
      a.addr := packet(5 + AXI4Parameters.AXI4AddrWidth, 6)
      a.prot := packet(5, 3)
      a
    }
  }
}
This post is licensed under CC BY 4.0 by the author.