diff --git a/src/main/scala/uk/ac/soton/ecs/can/core/Adder.scala b/src/main/scala/uk/ac/soton/ecs/can/core/Adder.scala
new file mode 100644
index 0000000000000000000000000000000000000000..d29dd8eac67d4dd25748f0e1795059345cf889b5
--- /dev/null
+++ b/src/main/scala/uk/ac/soton/ecs/can/core/Adder.scala
@@ -0,0 +1,11 @@
+package uk.ac.soton.ecs.can.core
+
+import chisel3._
+
+class Adder extends MultiIOModule {
+  val lhs = IO(Input(Vec(16, UInt(32.W))))
+  val rhs = IO(Input(Vec(16, UInt(32.W))))
+  val out = IO(Output(Vec(16, UInt(32.W))))
+
+  out := lhs.zip(rhs).map { case (lhs, rhs) => lhs + rhs }
+}
diff --git a/src/main/scala/uk/ac/soton/ecs/can/core/Xorer.scala b/src/main/scala/uk/ac/soton/ecs/can/core/Xorer.scala
new file mode 100644
index 0000000000000000000000000000000000000000..e553a6a1b7bfccbdbdc94802a01c6f6c31db946f
--- /dev/null
+++ b/src/main/scala/uk/ac/soton/ecs/can/core/Xorer.scala
@@ -0,0 +1,11 @@
+package uk.ac.soton.ecs.can.core
+
+import chisel3._
+
+class Xorer extends MultiIOModule {
+  val lhs = IO(Input(Vec(16, UInt(32.W))))
+  val rhs = IO(Input(Vec(16, UInt(32.W))))
+  val out = IO(Output(Vec(16, UInt(32.W))))
+
+  out := lhs.zip(rhs).map { case (lhs, rhs) => lhs ^ rhs }
+}
diff --git a/src/test/scala/uk/ac/soton/ecs/can/core/AdderTest.scala b/src/test/scala/uk/ac/soton/ecs/can/core/AdderTest.scala
new file mode 100644
index 0000000000000000000000000000000000000000..d90ada8bd94bf0521b25410905ae0cafb64bb00a
--- /dev/null
+++ b/src/test/scala/uk/ac/soton/ecs/can/core/AdderTest.scala
@@ -0,0 +1,27 @@
+package uk.ac.soton.ecs.can.core
+
+import org.scalatest._
+import chiseltest._
+import chisel3._
+import scala.util.Random
+import scala.math.abs
+
+class AdderTest extends FlatSpec with ChiselScalatestTester {
+  private val maxUInt = (Int.MaxValue.toLong << 1) | 1
+
+  behavior of "The Adder"
+
+  it should "sum the 16 32b unsigned integers" in {
+    test(new Adder) { c =>
+      val randomLhs = c.lhs.map(_ => abs(Random.nextInt))
+      val randomRhs = c.rhs.map(_ => abs(Random.nextInt))
+      val randomRes = randomLhs.zip(randomRhs).map { case (l, r) =>
+        (l.toLong + r.toLong) % maxUInt
+      }
+
+      c.lhs.zip(randomLhs).foreach { case (p, r) => p.poke(r.U) }
+      c.rhs.zip(randomRhs).foreach { case (p, r) => p.poke(r.U) }
+      c.out.zip(randomRes).foreach { case (p, r) => p.expect(r.U) }
+    }
+  }
+}
diff --git a/src/test/scala/uk/ac/soton/ecs/can/core/XorerTest.scala b/src/test/scala/uk/ac/soton/ecs/can/core/XorerTest.scala
new file mode 100644
index 0000000000000000000000000000000000000000..b37df1cbdd2da8096eebddb7df518283b5fb1990
--- /dev/null
+++ b/src/test/scala/uk/ac/soton/ecs/can/core/XorerTest.scala
@@ -0,0 +1,23 @@
+package uk.ac.soton.ecs.can.core
+
+import org.scalatest._
+import chiseltest._
+import chisel3._
+import scala.util.Random
+import scala.math.abs
+
+class XorerTest extends FlatSpec with ChiselScalatestTester {
+  behavior of "The Xorer"
+
+  it should "exclusive-or the 16 32b unsigned integers" in {
+    test(new Xorer) { c =>
+      val randomLhs = c.lhs.map(_ => abs(Random.nextInt))
+      val randomRhs = c.rhs.map(_ => abs(Random.nextInt))
+      val randomRes = randomLhs.zip(randomRhs).map { case (l, r) => l ^ r }
+
+      c.lhs.zip(randomLhs).foreach { case (p, r) => p.poke(r.U) }
+      c.rhs.zip(randomRhs).foreach { case (p, r) => p.poke(r.U) }
+      c.out.zip(randomRes).foreach { case (p, r) => p.expect(r.U) }
+    }
+  }
+}