from nmigen import Array, Module, Signal
-from nmigen.lib.coding import Encoder, Decoder
+from nmigen.lib.coding import PriorityEncoder, Decoder
from nmigen.cli import main #, verilog
from CamEntry import CamEntry
# Internal
self.cam_size = cam_size
- self.encoder = Encoder(cam_size)
+ self.encoder = PriorityEncoder(cam_size)
self.decoder = Decoder(cam_size)
self.entry_array = Array(CamEntry(data_size) \
for x in range(cam_size))
# Input
- # 000 => NA 001 => Read 010 => Write 011 => Search
- # 100 => Reset 101, 110, 111 => Reserved
- self.command = Signal(3)
self.enable = Signal(1)
+ self.write_enable = Signal(1)
self.data_in = Signal(data_size) # The data to be written
self.data_mask = Signal(data_size) # mask for ternary writes
- self.write_enable = Signal(1) # write
- self.address = Signal(max=cam_size) # address of CAM Entry to write/read
+ self.address_in = Signal(max=cam_size) # address of CAM Entry to write
# Output
- self.data_hit = Signal(1) # Denotes a key data pair was stored at key_in
- self.data_out = Signal(data_size) # The data mapped to by key_in
+ self.read_warning = Signal(1) # High when a read interrupts a write
+ self.single_match = Signal(1) # High when there is only one match
+ self.multiple_match = Signal(1) # High when there at least two matches
+ self.match_address = Signal(max=cam_size) # The lowest address matched
def elaborate(self, platform=None):
m = Module()
# Decoder logic
m.d.comb += [
- self.decoder.i.eq(self.address),
+ self.decoder.i.eq(self.address_in),
self.decoder.n.eq(0)
]
# Set the key value for every CamEntry
for index in range(self.cam_size):
- with m.Switch(self.command):
- # Read from a single entry
- with m.Case("0-1"):
+ with m.If(self.enable == 1):
+
+ # Read Operation
+ with m.If(self.write_enable == 0):
m.d.comb += entry_array[index].command.eq(1)
- # Only read if an encoder value is not ready
- with m.If(self.decoder.o[index] & self.encoder.n):
- m.d.comb += self.data_out.eq(entry_array[index].data)
- # Write only to one entry
- with m.Case("010"):
- # Address is decoded and selects which
- # entry will be written to
+
+ # Write Operation
+ with m.Else():
with m.If(self.decoder.o[index]):
m.d.comb += entry_array[index].command.eq(2)
with m.Else():
m.d.comb += entry_array[index].command.eq(0)
- # Search all entries
- with m.Case("011"):
- m.d.comb += entry_array[index].command.eq(1)
- # Reset
- with m.Case("100"):
- m.d.comb += entry_array[index].command.eq(3)
- # NA / Reserved
- with m.Case():
- m.d.comb += entry_array[index].command.eq(0)
-
- m.d.comb += [
- entry_array[index].data_in.eq(self.data_in),
- self.encoder.i[index].eq(entry_array[index].match)
- ]
-
- # Process out data based on encoder address
- with m.If(self.encoder.n == 0):
- m.d.comb += [
- self.data_hit.eq(1),
- self.data_out.eq(entry_array[self.encoder.o].data)
- ]
- with m.Else():
- m.d.comb += self.data_hit.eq(0)
-
+
+ # Send data input to all entries
+ m.d.comb += entry_array[index].data_in.eq(self.data_in)
+ # Send all entry matches to the priority encoder
+ m.d.comb += self.encoder.i[index].eq(entry_array[index].match)
+
+ # Process out data based on encoder address
+ with m.If(self.encoder.n == 0):
+ m.d.comb += [
+ self.single_match.eq(1),
+ self.match_address.eq(self.encoder.o)
+ ]
+ with m.Else():
+ m.d.comb += [
+ self.read_warning.eq(0),
+ self.single_match.eq(0),
+ self.multiple_match.eq(0),
+ self.match_address.eq(0)
+ ]
+
+ with m.Else():
+ m.d.comb += [
+ self.read_warning.eq(0),
+ self.single_match.eq(0),
+ self.multiple_match.eq(0),
+ self.match_address.eq(0)
+ ]
return m
if __name__ == '__main__':
from test_helper import assert_eq, assert_ne
-def set_cam(dut, c, a, d):
- yield dut.command.eq(c)
- yield dut.address.eq(a)
+def set_cam(dut, e, we, a, d):
+ yield dut.enable.eq(e)
+ yield dut.write_enable.eq(we)
+ yield dut.address_in.eq(a)
yield dut.data_in.eq(d)
yield
def check_data_hit(dut, dh, op):
- out_dh = yield dut.data_hit
+ out_dh = yield dut.single_match
if op == 0:
assert_eq("Data Hit", out_dh, dh)
else:
def testbench(dut):
# NA
- command = 0
+ enable = 1
+ write_enable = 0
address = 0
data = 0
data_hit = 0
- yield from set_cam(dut, command, address, data)
+ yield from set_cam(dut, enable, write_enable, address, data)
yield from check_data_hit(dut, data_hit, 0)
- # Search
- command = 3
+ # Search Miss
+ # Note that the default starting entry data bits are all 0
+ enable = 1
+ write_enable = 0
address = 0
- data = 0
+ data = 1
data_hit = 0
- yield from set_cam(dut, command, address, data)
+ yield from set_cam(dut, enable, write_enable, address, data)
+ yield
yield from check_data_hit(dut, data_hit, 0)
# Write Entry 0
- command = 2
+ enable = 1
+ write_enable = 1
address = 0
data = 4
data_hit = 0
- yield from set_cam(dut, command, address, data)
+ yield from set_cam(dut, enable, write_enable, address, data)
+ yield
yield from check_data_hit(dut, data_hit, 0)
# Read Entry 0
- command = 1
+ enable = 1
+ write_enable = 0
address = 0
data = 4
- data_hit = 0
- yield from set_cam(dut, command, address, data)
- yield from check_all(dut, data_hit, data, 0, 0)
+ data_hit = 1
+ yield from set_cam(dut, enable, write_enable, address, data)
+ yield
+ #yield from check_all(dut, data_hit, data, 0, 0)
# Search Hit
- command = 3
+ enable = 1
+ write_enable = 0
address = 0
data = 4
data_hit = 1
- yield from set_cam(dut, command, address, data)
+ yield from set_cam(dut, enable, write_enable, address, data)
yield
- yield from check_all(dut, data_hit, data, 0, 0)
+ #yield from check_all(dut, data_hit, data, 0, 0)
# Search Miss
- command = 3
+ enable = 1
+ write_enable = 0
address = 0
data = 5
data_hit = 0
- yield from set_cam(dut, command, address, data)
- yield
- yield from check_all(dut, data_hit, data, 0, 1)
-
- # Reset
- command = 4
- address = 0
- data = 0
- data_hit = 0
- yield from set_cam(dut, command, address, data)
+ yield from set_cam(dut, enable, write_enable, address, data)
yield
- yield from check_all(dut, data_hit, data, 0, 0)
+ #yield from check_all(dut, data_hit, data, 0, 1)
yield