3

It would be nice to Vec[Mem] for say set-associative caches.

Unfortunately Chisel doesn't support Vec[Mem] construct:

val tag_ram2    = Vec.fill(num_ways) {Mem(new TagType(), num_sets , seqRead = true )}

Indeed:

inferred type arguments [Chisel.Mem[cache.TagType]] do not conform to method fill's type     parameter bounds [T <: Chisel.Data]
[error] Error occurred in an application involving default arguments.
[error]     val tag_ram2    = Vec.fill(num_ways) {Mem(new TagType(), num_sets , seqRead = true )}
[error]                               ^
[error] /home/asamoilov/work/projects/my-chisel/Cache.scala:139: type mismatch;
[error]  found   : Chisel.Mem[cache.TagType]
[error]  required: T
[error] Error occurred in an application involving default arguments.
[error]     val tag_ram2    = Vec.fill(num_ways) {Mem(new TagType(), num_sets , seqRead = true )}

However a simple workaround works fine:

val tag_ram2    = Array.fill(num_ways) {Mem(new TagType(), num_sets , seqRead = true )}
[...]
    is (read_tag) {

        set_idx := req_idx % UInt(num_sets) // FIXME
        for (way_no <- 0 until num_ways) {
            tag_read_vec(way_no) := tag_ram2(way_no)(set_idx)
        }
        controller_state := compare_tag
    }

And for writing tags (of cause under some when(...) clause)

            for (way_no <- 0 until num_ways) {
                when (UInt(way_no) === way_idx) {
                    printf("writing to way %d set %d tag %x\n", way_idx, set_idx, tag_write.toBits)
                    tag_ram2(way_no)(set_idx) := tag_write
                }
            }

Comments, proposals for improving proposed scheme? Thanks!

  • Feature requests are more likely to be seen and responded to at (https://github.com/ucb-bar/chisel/issues) or maybe also (https://groups.google.com/forum/#!forum/chisel-users). What I do is set up a for loop, and anonymously define each bank from within the for loop. For memory banking, I've found it perfectly fine (maybe even better?) than trying to use what would effectively be 2d arrays. – Chris Oct 29 '13 at 22:33
  • You are quite right - this is effectively a 2d array for keeping n_ways X n_sets tags and data. May I have a look at your implementation of memory banking? I'm new to Chisel and looking for the best Chisel's style guides. Thanks! – Alexander Samoilov Oct 30 '13 at 12:25
  • 1
    Ok, found 2-bank memory implementation here in your code on github: https://github.com/ucb-bar/riscv-sodor/blob/master/src/rv32_3stage/memory.scala – Alexander Samoilov Oct 30 '13 at 21:28
  • Yah, I'm not super proud of that though. I'm explicitly naming each bank - and thus tied to only having 2 banks. That's because I have to match a weird use-case in which the external host-target interface (HTIF) needs to read/write the scratchpad, which is done in 16 byte chunks vs. the usual 8 byte chunks from the processor (and I got lazy and didn't want to handle write-masks and shifting/masking read data, so each "line" of memory is one word wide). So the processor reads/writes 1 bank, and the HTIF can read/write across both banks in a single cycle. – Chris Oct 30 '13 at 21:36

1 Answers1

1

For the tags array, try using a 1-d vector of Bits that's (n_tag_sz*n_ways) in width. On a cache access, you read out the entire row anyways, and you want to store that in something as dense as possible. So like this:

val tag_array = Mem(Bits(width = tagbits*n_ways), n_sets, seqRead = true)  

And here's a snippet of psuedo-code for an i-cache's memory banks, which covers 3 cycles (s0,s1,s2) for ifgen, ic_access, and ic_response:

val s1_tag_match = Vec.fill(n_ways){Bool()}
val s2_tag_hit = Vec.fill(n_ways){Bool()}
val s2_dout = Vec.fill(n_ways){Reg(Bits())}

for (i <- 0 until n_ways) 
{
   // notice each cycle of refill gets its own line
   val data_array = Mem(Bits(width = n_code_width), n_sets*REFILL_CYCLES, seqRead = true)
   val s1_raddr = Reg(UInt())
   // refill
   when (io.mem.resp.valid && repl_way === UInt(i)) 
   {
      data_array(Cat(s2_idx,rf_cnt)) := io.mem.resp.bits.data
   }
   // read enable
   .elsewhen (s0_valid) 
   {
      s1_raddr := s0_raddr
   }

   // read
   when (s1_valid && s1_tag_match(i) && ready) 
   { 
      s2_dout(i) := data_array(s1_raddr) 
   }
 }

 io.resp.bits.data := Mux1H(s2_tag_hit, s2_dout)
Chris
  • 3,827
  • 22
  • 30