phaser-ags / chisel /src /test /NandTest.scala
SNAPKITTYWEST's picture
October 2026 main drop: mirror from GitHub
829b19c verified
Raw History Blame Contribute Delete
8.87 kB
package nand
import chisel3._
import chiseltest._
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
// ═══════════════════════════════════════════════════════════════════
// TEST HARNESSES β€” ChiselTest verification for all NAND-derived
// modules. Truth tables match SPICE circuit simulation results.
// ═══════════════════════════════════════════════════════════════════
class Nand2Test extends AnyFlatSpec with ChiselScalatestTester with Matchers {
behavior of "Nand2"
it should "match NAND truth table" in {
test(new Nand2) { dut =>
for (a <- Seq(false, true); b <- Seq(false, true)) {
dut.io.a.poke(a.B)
dut.io.b.poke(b.B)
dut.clock.step(1)
dut.io.y.expect(!(a && b).B)
}
}
}
}
class Nand3Test extends AnyFlatSpec with ChiselScalatestTester with Matchers {
behavior of "Nand3"
it should "match 3-input NAND truth table" in {
test(new Nand3) { dut =>
for {
a <- Seq(false, true)
b <- Seq(false, true)
c <- Seq(false, true)
} {
dut.io.a.poke(a.B)
dut.io.b.poke(b.B)
dut.io.c.poke(c.B)
dut.clock.step(1)
dut.io.y.expect(!(a && b && c).B)
}
}
}
}
class InvFromNandTest extends AnyFlatSpec with ChiselScalatestTester with Matchers {
behavior of "InvFromNand"
it should "invert" in {
test(new InvFromNand) { dut =>
dut.io.a.poke(false.B); dut.clock.step(1); dut.io.y.expect(true.B)
dut.io.a.poke(true.B); dut.clock.step(1); dut.io.y.expect(false.B)
}
}
}
class And2FromNandTest extends AnyFlatSpec with ChiselScalatestTester with Matchers {
behavior of "And2FromNand"
it should "match AND truth table" in {
test(new And2FromNand) { dut =>
for (a <- Seq(false, true); b <- Seq(false, true)) {
dut.io.a.poke(a.B)
dut.io.b.poke(b.B)
dut.clock.step(1)
dut.io.y.expect((a && b).B)
}
}
}
}
class Or2FromNandTest extends AnyFlatSpec with ChiselScalatestTester with Matchers {
behavior of "Or2FromNand"
it should "match OR truth table" in {
test(new Or2FromNand) { dut =>
for (a <- Seq(false, true); b <- Seq(false, true)) {
dut.io.a.poke(a.B)
dut.io.b.poke(b.B)
dut.clock.step(1)
dut.io.y.expect((a || b).B)
}
}
}
}
class Xor2FromNandTest extends AnyFlatSpec with ChiselScalatestTester with Matchers {
behavior of "Xor2FromNand"
it should "match XOR truth table" in {
test(new Xor2FromNand) { dut =>
for (a <- Seq(false, true); b <- Seq(false, true)) {
dut.io.a.poke(a.B)
dut.io.b.poke(b.B)
dut.clock.step(1)
dut.io.y.expect((a ^ b).B)
}
}
}
}
class Xnor2FromNandTest extends AnyFlatSpec with ChiselScalatestTester with Matchers {
behavior of "Xnor2FromNand"
it should "match XNOR truth table" in {
test(new Xnor2FromNand) { dut =>
for (a <- Seq(false, true); b <- Seq(false, true)) {
dut.io.a.poke(a.B)
dut.io.b.poke(b.B)
dut.clock.step(1)
dut.io.y.expect(!(a ^ b).B)
}
}
}
}
class Mux2FromNandTest extends AnyFlatSpec with ChiselScalatestTester with Matchers {
behavior of "Mux2FromNand"
it should "select a when sel=0, b when sel=1" in {
test(new Mux2FromNand) { dut =>
for (sel <- Seq(false, true)) {
for (a <- Seq(false, true); b <- Seq(false, true)) {
dut.io.a.poke(a.B)
dut.io.b.poke(b.B)
dut.io.sel.poke(sel.B)
dut.clock.step(1)
dut.io.y.expect((if (sel) b else a).B)
}
}
}
}
}
// ─── Sequential tests ─────────────────────────────────────────────
class SrLatchFromNandTest extends AnyFlatSpec with ChiselScalatestTester with Matchers {
behavior of "SrLatchFromNand"
it should "SET, HOLD, RESET correctly" in {
test(new SrLatchFromNand) { dut =>
// Initial: hold (both inactive-high)
dut.io.s_n.poke(true.B)
dut.io.r_n.poke(true.B)
dut.clock.step(1)
// SET: S'=0
dut.io.s_n.poke(false.B)
dut.clock.step(2)
dut.io.q.expect(true.B)
dut.io.qbar.expect(false.B)
// HOLD
dut.io.s_n.poke(true.B)
dut.io.r_n.poke(true.B)
dut.clock.step(2)
dut.io.q.expect(true.B)
// RESET: R'=0
dut.io.r_n.poke(false.B)
dut.clock.step(2)
dut.io.q.expect(false.B)
dut.io.qbar.expect(true.B)
// HOLD after reset
dut.io.r_n.poke(true.B)
dut.clock.step(2)
dut.io.q.expect(false.B)
}
}
}
class DFlipFlopFromNandTest extends AnyFlatSpec with ChiselScalatestTester with Matchers {
behavior of "DFlipFlopFromNand"
it should "capture D on rising clock edge" in {
test(new DFlipFlopFromNand) { dut =>
// Set D=1, clock low β†’ nothing captured
dut.io.d.poke(true.B)
dut.io.clk.poke(false.B)
dut.clock.step(2)
// Rising edge β†’ capture D=1
dut.io.clk.poke(true.B)
dut.clock.step(2)
dut.io.q.expect(true.B)
// Change D while clock high β†’ Q holds
dut.io.d.poke(false.B)
dut.clock.step(2)
dut.io.q.expect(true.B)
// Falling edge β†’ Q still holds
dut.io.clk.poke(false.B)
dut.clock.step(2)
dut.io.q.expect(true.B)
// Next rising edge with D=0 β†’ capture 0
dut.io.clk.poke(true.B)
dut.clock.step(2)
dut.io.q.expect(false.B)
}
}
}
// ─── Arithmetic tests ─────────────────────────────────────────────
class HalfAdderFromNandTest extends AnyFlatSpec with ChiselScalatestTester with Matchers {
behavior of "HalfAdderFromNand"
it should "match half-adder truth table" in {
test(new HalfAdderFromNand) { dut =>
Map(
(false, false) -> (false, false),
(false, true) -> (true, false),
(true, false) -> (true, false),
(true, true) -> (false, true),
).foreach { case ((a, b), (sum, cout)) =>
dut.io.a.poke(a.B)
dut.io.b.poke(b.B)
dut.clock.step(1)
dut.io.sum.expect(sum.B)
dut.io.cout.expect(cout.B)
}
}
}
}
class FullAdderFromNandTest extends AnyFlatSpec with ChiselScalatestTester with Matchers {
behavior of "FullAdderFromNand"
it should "match full-adder truth table" in {
test(new FullAdderFromNand) { dut =>
for {
a <- Seq(false, true)
b <- Seq(false, true)
cin <- Seq(false, true)
} {
dut.io.a.poke(a.B)
dut.io.b.poke(b.B)
dut.io.cin.poke(cin.B)
dut.clock.step(1)
val total = (if (a) 1 else 0) + (if (b) 1 else 0) + (if (cin) 1 else 0)
dut.io.sum.expect((total % 2 == 1).B)
dut.io.cout.expect((total >= 2).B)
}
}
}
}
class RippleCarryAdder8Test extends AnyFlatSpec with ChiselScalatestTester with Matchers {
behavior of "RippleCarryAdder8"
it should "add correctly for various operand pairs" in {
test(new RippleCarryAdder(8)) { dut =>
val testCases = Seq(
(0, 0, 0),
(1, 1, 0),
(42, 73, 0),
(127, 128 % 256, 0),
(200, 55, 0),
(255, 1, 0),
(255, 255, 0),
(255, 255, 1),
)
for ((a, b, cin) <- testCases) {
dut.io.a.poke(a.U)
dut.io.b.poke(b.U)
dut.io.cin.poke((cin == 1).B)
dut.clock.step(1)
val expected = (a + b + cin) % 256
val carryOut = (a + b + cin) >= 256
dut.io.sum.expect(expected.U)
dut.io.cout.expect(carryOut.B)
}
}
}
}
class AluFromNand8Test extends AnyFlatSpec with ChiselScalatestTester with Matchers {
behavior of "AluFromNand8"
it should "perform AND, OR, XOR, ADD correctly" in {
test(new AluFromNand(8)) { dut =>
// AND: op=00
dut.io.op.poke(0.U)
dut.io.a.poke(0xFF.U); dut.io.b.poke(0x55.U)
dut.clock.step(1)
dut.io.y.expect(0x55.U)
// OR: op=01
dut.io.op.poke(1.U)
dut.io.a.poke(0x0F.U); dut.io.b.poke(0xF0.U)
dut.clock.step(1)
dut.io.y.expect(0xFF.U)
// XOR: op=10
dut.io.op.poke(2.U)
dut.io.a.poke(0xFF.U); dut.io.b.poke(0x55.U)
dut.clock.step(1)
dut.io.y.expect(0xAA.U)
// ADD: op=11
dut.io.op.poke(3.U)
dut.io.cin.poke(false.B)
dut.io.a.poke(42.U); dut.io.b.poke(73.U)
dut.clock.step(1)
dut.io.y.expect(115.U)
}
}
}