diff --git a/src/main/scala/uk/ac/soton/ecs/can/core/ControlWord.scala b/src/main/scala/uk/ac/soton/ecs/can/core/ControlWord.scala
index ab63be09a2ce59506588cd9606986dd3dab0b8a0..8760cb18c5661aa1826c840c40f1e76eef3d7718 100644
--- a/src/main/scala/uk/ac/soton/ecs/can/core/ControlWord.scala
+++ b/src/main/scala/uk/ac/soton/ecs/can/core/ControlWord.scala
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: 2021 Minyong Li <ml10g20@soton.ac.uk>
+// SPDX-License-Identifier: CERN-OHL-W-2.0
+
 package uk.ac.soton.ecs.can.core
 
 import chisel3._
@@ -6,13 +9,18 @@ class ControlWord(addrWidth: Int, immWidth: Int = 8) extends Bundle {
   val immediate = UInt(immWidth.W)
   val absoluteBranch = Bool()
   val relativeBranch = Bool()
-  val ramAddressRead = Vec(2, UInt(addrWidth.W))
-  val ramAddressWrite = UInt(addrWidth.W)
+  val ramReadAddress = Vec(2, UInt(addrWidth.W))
+  val ramWriteAddress = UInt(addrWidth.W)
   val fillConstant = Bool()
   val incrementBlockCount = Bool()
   val roundLoop = Bool()
   val addFrom = Bool()
   val xorFrom = Bool()
-  val writeBackInit = Bool()
-  val writeBackRound = Bool()
+  val writeBackFromInit = Bool()
+  val writeBackFromRound = Bool()
+}
+
+object ControlWord {
+  def apply(addrWidth: Int, immWidth: Int = 8) =
+    new ControlWord(addrWidth, immWidth)
 }
diff --git a/src/main/scala/uk/ac/soton/ecs/can/core/ProgramMemory.scala b/src/main/scala/uk/ac/soton/ecs/can/core/ProgramMemory.scala
index 1575d7f5444ece5e90635752980f7dd2aff237d8..f5997254dd9a39dccab1c3a7aba431683a86993a 100644
--- a/src/main/scala/uk/ac/soton/ecs/can/core/ProgramMemory.scala
+++ b/src/main/scala/uk/ac/soton/ecs/can/core/ProgramMemory.scala
@@ -8,7 +8,7 @@ import chisel3._
 class ProgramMemory(
     addrWidth: Int,
     cwWidth: Int,
-    size: Int,
+    nWords: Int,
     syncMem: Boolean = true
 ) extends MultiIOModule {
   val br = IO(new Bundle {
@@ -17,20 +17,21 @@ class ProgramMemory(
     val addr = Input(UInt(addrWidth.W))
   })
   val cw = IO(Output(UInt(cwWidth.W)))
+
+  val read = IO(new Bundle {
+    val addr = Input(UInt(addrWidth.W))
+    val data = Output(UInt(cwWidth.W))
+  })
   val write = IO(new Bundle {
     val en = Input(Bool())
     val addr = Input(UInt(addrWidth.W))
     val data = Input(UInt(cwWidth.W))
   })
 
-  val mem =
-    if (syncMem) SyncReadMem(size, UInt(cwWidth.W))
-    else Mem(size, UInt(cwWidth.W))
-  val pc = RegInit(0.U(addrWidth.W))
-
-  when(write.en) {
-    mem(write.addr) := write.data
-  }
+  private val mem =
+    if (syncMem) SyncReadMem(nWords, UInt(cwWidth.W))
+    else Mem(nWords, UInt(cwWidth.W))
+  private val pc = RegInit(0.U(addrWidth.W))
 
   when(br.abs) {
     pc := br.addr.asUInt()
@@ -41,4 +42,10 @@ class ProgramMemory(
   }
 
   cw := mem(pc)
+
+  read.data := mem(read.addr)
+
+  when(write.en) {
+    mem(write.addr) := write.data
+  }
 }
diff --git a/src/test/scala/uk/ac/soton/ecs/can/core/ProgramMemoryTest.scala b/src/test/scala/uk/ac/soton/ecs/can/core/ProgramMemoryTest.scala
index 0ac5312126b0212c743b473ef70aefea71f3466b..96ffa8961ac3f5d5cb3168384766e486d7b3365a 100644
--- a/src/test/scala/uk/ac/soton/ecs/can/core/ProgramMemoryTest.scala
+++ b/src/test/scala/uk/ac/soton/ecs/can/core/ProgramMemoryTest.scala
@@ -7,28 +7,27 @@ import org.scalatest._
 import chiseltest._
 import chisel3._
 
+import scala.util.Random
+import scala.math.pow
+
 class ProgramMemoryTest extends FlatSpec with ChiselScalatestTester {
   private val addrWidth = 8
-  private val cwWidth = 16
-  private val memMap = Seq(
-    "h00".U(addrWidth.W) -> "h0000".U(cwWidth.W),
-    "h01".U(addrWidth.W) -> "h1234".U(cwWidth.W),
-    "h02".U(addrWidth.W) -> "h2468".U(cwWidth.W),
-    "h03".U(addrWidth.W) -> "h369a".U(cwWidth.W),
-    "h04".U(addrWidth.W) -> "h4c2d".U(cwWidth.W),
-    "h05".U(addrWidth.W) -> "h59f7".U(cwWidth.W),
-    "h06".U(addrWidth.W) -> "h6efc".U(cwWidth.W),
-    "h07".U(addrWidth.W) -> "h7fff".U(cwWidth.W)
+  private val immWidth = 8
+  private val cwWidth = ControlWord(addrWidth, immWidth).getWidth
+  private val memMap = Seq.fill(8)(
+    Random
+      .nextInt(pow(2, cwWidth).toInt - 1)
+      .asUInt(cwWidth.W)
   )
-  private val size = memMap.length
+  private val nWords = memMap.length
 
-  private def initMemory(pm: ProgramMemory): Unit = {
+  private def initMemory(pm: ProgramMemory) {
     pm.reset.poke(true.B)
     pm.write.en.poke(true.B)
 
-    memMap.foreach { m =>
-      pm.write.addr.poke(m._1)
-      pm.write.data.poke(m._2)
+    memMap.zipWithIndex.foreach { case (data, addr) =>
+      pm.write.addr.poke(addr.U(addrWidth.W))
+      pm.write.data.poke(data)
       pm.clock.step()
     }
 
@@ -39,114 +38,114 @@ class ProgramMemoryTest extends FlatSpec with ChiselScalatestTester {
   behavior of "The Program Memory"
 
   it should "be writable and readable as PC increments" in {
-    test(new ProgramMemory(addrWidth, cwWidth, size)) { c =>
+    test(new ProgramMemory(addrWidth, cwWidth, nWords)) { c =>
       c.br.abs.poke(false.B)
       c.br.rel.poke(false.B)
-      c.br.addr.poke(0.U(addrWidth.W))
+      c.br.addr.poke(0.U(immWidth.W))
 
       initMemory(c)
 
-      c.cw.expect("h0000".U(cwWidth.W))
+      c.cw.expect(memMap.head)
       c.clock.step()
 
       // NOTE: FPGA block RAM is synchronous-read. At this moment a new value
       // has been fetched, but this 1-cycle delay exists because it hasn't been
       // stored into the read register yet.
-      c.cw.expect("h0000".U(cwWidth.W))
+      c.cw.expect(memMap.head)
       c.clock.step()
 
-      memMap.takeRight(memMap.length - 1).foreach { m =>
-        c.cw.expect(m._2)
+      memMap.tail.foreach { data =>
+        c.cw.expect(data)
         c.clock.step()
       }
     }
 
-    test(new ProgramMemory(addrWidth, cwWidth, size, false)) { c =>
+    test(new ProgramMemory(addrWidth, cwWidth, nWords, false)) { c =>
       c.br.abs.poke(false.B)
       c.br.rel.poke(false.B)
-      c.br.addr.poke(0.U(addrWidth.W))
+      c.br.addr.poke(0.U(immWidth.W))
 
       initMemory(c)
 
-      memMap.foreach { m =>
-        c.cw.expect(m._2)
+      memMap.foreach { data =>
+        c.cw.expect(data)
         c.clock.step()
       }
     }
   }
 
   it should "do relative branching correctly" in {
-    test(new ProgramMemory(addrWidth, cwWidth, size)) { c =>
+    test(new ProgramMemory(addrWidth, cwWidth, nWords)) { c =>
       c.br.abs.poke(false.B)
       c.br.rel.poke(false.B)
-      c.br.addr.poke(0.U(addrWidth.W))
+      c.br.addr.poke(0.U(immWidth.W))
 
       initMemory(c)
 
-      c.cw.expect("h0000".U(cwWidth.W))
+      c.cw.expect(memMap.head)
       c.clock.step()
 
-      memMap.take(3).foreach { m =>
-        c.cw.expect(m._2)
+      memMap.take(3).foreach { data =>
+        c.cw.expect(data)
         c.clock.step()
       }
 
-      // @ 0x03 -> 0x369a
-      c.cw.expect("h369a".U(cwWidth.W))
+      // @ 0x03
+      c.cw.expect(memMap(3))
 
-      // > 0x06 -> 0x6efc
+      // > 0x06
       // NOTE: Because of the synchronous BRAM, a 1-cycle delay slot is
       // introduced. At this moment 0x04 has been fetched, so the offset should
       // be calculated based on 0x04 rather than 0x03. Here 4 + 2 = 6.
-      c.br.addr.poke(2.U(addrWidth.W))
+      c.br.addr.poke(2.U(immWidth.W))
       c.br.rel.poke(true.B)
       c.clock.step()
       c.br.rel.poke(false.B)
 
       // 0x04 is now present, but 0x06 has been fetched
-      c.cw.expect("h4c2d".U(cwWidth.W))
+      c.cw.expect(memMap(4))
       c.clock.step()
 
       // @ 0x06 now
-      memMap.takeRight(2).foreach { m =>
-        c.cw.expect(m._2)
+      memMap.takeRight(2).foreach { data =>
+        c.cw.expect(data)
         c.clock.step()
       }
     }
   }
 
   it should "do absolute branching correctly" in {
-    test(new ProgramMemory(addrWidth, cwWidth, size)) { c =>
+    test(new ProgramMemory(addrWidth, cwWidth, nWords)) { c =>
       c.br.abs.poke(false.B)
       c.br.rel.poke(false.B)
-      c.br.addr.poke(0.U(addrWidth.W))
+      c.br.addr.poke(0.U(immWidth.W))
 
       initMemory(c)
 
-      c.cw.expect("h0000".U(cwWidth.W))
+      c.cw.expect(memMap.head)
       c.clock.step()
 
-      memMap.take(5).foreach { m =>
-        c.cw.expect(m._2)
+      memMap.take(5).foreach { data =>
+        c.cw.expect(data)
         c.clock.step()
       }
 
-      // @ 0x05 -> 0x59f7
-      c.cw.expect("h59f7".U(cwWidth.W))
+      // @ 0x05
+      c.cw.expect(memMap(5))
 
-      // > 0x01 -> 0x1234
-      c.br.addr.poke("h01".U(addrWidth.W))
+      // > 0x01
+      c.br.addr.poke("h01".U(immWidth.W))
       c.br.abs.poke(true.B)
       c.clock.step()
       c.br.abs.poke(false.B)
 
       // Delay slot: 0x06 will present no matter what
-      c.cw.expect("h6efc".U(cwWidth.W))
+      c.cw.expect(memMap(6))
       c.clock.step()
 
       // @ 0x01 now
-      memMap.takeRight(memMap.length - 1).foreach { m =>
-        c.cw.expect(m._2)
+      memMap.tail.foreach { data =>
+        c.cw.expect(data)
         c.clock.step()
       }
     }