Compare commits

..

No commits in common. "main" and "xenia/test1" have entirely different histories.

5 changed files with 21 additions and 222 deletions

View File

@ -9,14 +9,12 @@ You can also include images in this folder and reference them in the markdown. E
## How it works
This is a minimal SHA-256 hash core implemented in a single-cycle-round architecture. TODO: expand
on this
Explain how your project works
## How to test
TODO: write instructions
Explain how to use your project
## External hardware
No external hardware is needed besides some method of interacting with the bus to transfer commands
and data.
List external hardware used in your project (e.g. PMOD, LED display, etc), if any

View File

@ -8,7 +8,7 @@ project:
clock_hz: 50000000 # Clock frequency in Hz (or 0 if not applicable)
# How many tiles your design occupies? A single tile is about 167x108 uM.
tiles: "2x2" # Valid values: 1x1, 1x2, 2x2, 3x2, 4x2, 6x2 or 8x2
tiles: "1x1" # Valid values: 1x1, 1x2, 2x2, 3x2, 4x2, 6x2 or 8x2
# Your top module name must start with "tt_um_". Make it unique by including your github username:
top_module: "tt_um_xeniarose_sha256"

View File

@ -12,7 +12,7 @@
# PL_TARGET_DENSITY - You can increase this if Global Placement fails with error GPL-0302.
# Users have reported that values up to 0.8 worked well for them.
set ::env(PL_TARGET_DENSITY) 0.8
set ::env(PL_TARGET_DENSITY) 0.6
# CLOCK_PERIOD - Increase this in case you are getting setup time violations.
# The value is in nanoseconds, so 20ns == 50MHz.

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2024 xenia dragon
* Copyright (c) 2024 xenia dragon
* SPDX-License-Identifier: Apache-2.0
*
* [usagi holding floppy disk.png] i'll just warn you right now, i don't know how to use a computer

View File

@ -1,96 +1,13 @@
# SPDX-FileCopyrightText: Copyright (c) 2024 xenia dragon
# SPDX-License-Identifier: Apache-2.0
import hashlib
import struct
# SPDX-FileCopyrightText: © 2024 Tiny Tapeout
# SPDX-License-Identifier: MIT
import cocotb
from cocotb.clock import Clock
from cocotb.triggers import ClockCycles
IO_RWSEL = 0b0100_0000
IO_CLK = 0b1000_0000
MODE_READ = 1
MODE_WRITE = 2
class TestMeta:
def __init__(self):
self.mode = None
async def io_write(dut, meta: TestMeta, addr: int, word: int) -> None:
assert addr & 0b11 == 0
if meta.mode != MODE_WRITE:
dut.ui_in.value = 0
await ClockCycles(dut.clk, 1)
meta.mode = MODE_WRITE
assert (dut.uo_out.value >> 1) & 1 == 0
assert dut.uio_oe.value == 0x00
await ClockCycles(dut.clk, 1)
for i in range(4):
# dut._log.info("writing %s=%s",
# f"{addr | i:02x}",
# f"{(word >> (i*8)) & 0xFF:08b}")
dut.uio_in.value = (word >> (i*8)) & 0xFF
dut.ui_in.value = IO_CLK | addr | i
await ClockCycles(dut.clk, 1)
dut.uio_in.value = (word >> (i*8)) & 0xFF
dut.ui_in.value = addr | i
await ClockCycles(dut.clk, 1)
async def io_read(dut, meta: TestMeta, addr: int) -> int:
assert addr & 0b11 == 0
if meta.mode != MODE_READ:
dut.ui_in.value = IO_RWSEL
await ClockCycles(dut.clk, 1)
meta.mode = MODE_READ
assert (dut.uo_out.value >> 1) & 1 == 1
assert dut.uio_oe.value == 0xff
await ClockCycles(dut.clk, 1)
word = 0
for i in range(4):
dut.ui_in.value = IO_CLK | IO_RWSEL | addr | i
await ClockCycles(dut.clk, 1)
dut.ui_in.value = IO_RWSEL | addr | i
await ClockCycles(dut.clk, 1)
word |= (dut.uio_out.value & 0xFF) << (i*8)
# dut._log.info("reading %s=%s",
# f"{addr | i:02x}",
# dut.uio_out.value)
return word
async def io_trigger(dut, meta: TestMeta) -> None:
if meta.mode != MODE_WRITE:
dut.ui_in.value = 0
await ClockCycles(dut.clk, 1)
meta.mode = MODE_WRITE
assert (dut.uo_out.value >> 1) & 1 == 0
assert dut.uio_oe.value == 0x00
await ClockCycles(dut.clk, 1)
dut.uio_in.value = 0
dut.ui_in.value = IO_CLK | 63
await ClockCycles(dut.clk, 1)
dut.uio_in.value = 0
dut.ui_in.value = 63
await ClockCycles(dut.clk, 1)
async def test_init(dut):
meta = TestMeta()
@cocotb.test()
async def test_project(dut):
dut._log.info("Start")
# Set the clock period to 10 us (100 KHz)
@ -106,134 +23,18 @@ async def test_init(dut):
await ClockCycles(dut.clk, 10)
dut.rst_n.value = 1
return meta
dut._log.info("Test project behavior")
# Set the input values you want to test
dut.ui_in.value = 20
dut.uio_in.value = 30
@cocotb.test()
async def test_registers(dut):
meta = await test_init(dut)
dut._log.info("Test registers")
# Wait for one clock cycle to see the output values
await ClockCycles(dut.clk, 1)
for addr in range(0, 40, 4):
dut._log.info("Test register %s", addr)
for val in [0x1312621, 0, 0xffffffff, 0x13371337, 0x55aa9966]:
await io_write(dut, meta, addr, val)
rval = await io_read(dut, meta, addr)
assert val == rval
# The following assersion is just an example of how to check the output values.
# Change it to match the actual expected output of your module:
assert dut.uo_out.value == 50
K = [0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2]
H = [0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19]
def _rotr(x, y):
return ((x >> y) | (x << (32 - y))) & 0xFFFFFFFF
def _maj(x, y, z):
return (x & y) ^ (x & z) ^ (y & z)
def _ch(x, y, z):
return (x & y) ^ ((~x) & z)
@cocotb.test()
async def test_sha_round(dut):
meta = await test_init(dut)
dut._log.info("Test SHA-2 compression cycle")
for w in [0x1312621, 0, 0xffffffff, 0x13371337, 0x55aa9966]:
for i in range(8):
await io_write(dut, meta, i*4, H[i])
await io_write(dut, meta, 32, w)
await io_write(dut, meta, 36, K[0])
await io_trigger(dut, meta)
a, b, c, d, e, f, g, h = H
s0 = _rotr(a, 2) ^ _rotr(a, 13) ^ _rotr(a, 22)
t2 = s0 + _maj(a, b, c)
s1 = _rotr(e, 6) ^ _rotr(e, 11) ^ _rotr(e, 25)
t1 = h + s1 + _ch(e, f, g) + K[0] + w
h = g
g = f
f = e
e = (d + t1) & 0xFFFFFFFF
d = c
c = b
b = a
a = (t1 + t2) & 0xFFFFFFFF
expected = [a, b, c, d, e, f, g, h]
for i in range(8):
actual = await io_read(dut, meta, i*4)
assert actual == expected[i]
async def test_one_sha_full(dut, meta, message: bytes) -> None:
reference_hash = hashlib.sha256(message).digest()
mdi = len(message) & 0x3F
length = struct.pack('!Q', len(message) << 3)
if mdi < 56:
padlen = 55 - mdi
else:
padlen = 119 - mdi
message = message + b'\x80' + (b'\x00' * padlen) + length
assert len(message) % 64 == 0
h = list(H)
for chunk_idx in range(0, len(message), 64):
chunk = message[chunk_idx:chunk_idx+64]
for i in range(8):
await io_write(dut, meta, i*4, h[i])
w = [0] * 64
w[0:16] = struct.unpack('!16L', chunk)
for i in range(16, 64):
s0 = _rotr(w[i-15], 7) ^ _rotr(w[i-15], 18) ^ (w[i-15] >> 3)
s1 = _rotr(w[i-2], 17) ^ _rotr(w[i-2], 19) ^ (w[i-2] >> 10)
w[i] = (w[i-16] + s0 + w[i-7] + s1) & 0xFFFFFFFF
for i in range(64):
await io_write(dut, meta, 32, w[i])
await io_write(dut, meta, 36, K[i])
await io_trigger(dut, meta)
for i in range(8):
h[i] = (h[i] + (await io_read(dut, meta, i*4))) & 0xFFFFFFFF
result = struct.pack("!IIIIIIII", *h)
assert result == reference_hash
@cocotb.test()
async def test_sha_full(dut):
meta = await test_init(dut)
dut._log.info("Test SHA-2 full hash")
await test_one_sha_full(dut, meta, b"")
await test_one_sha_full(dut, meta, b"message digest")
await test_one_sha_full(dut, meta, b"trans rights")
await test_one_sha_full(dut, meta, b"abababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababab")
# Keep testing the module by changing the input values, waiting for
# one or more clock cycles, and asserting the expected output values.