Skip to content

Commit

Permalink
Impove BIST hammer only attacks to support any number of rwos up to 32
Browse files Browse the repository at this point in the history
Signed-off-by: Maciej Dudek <[email protected]>
  • Loading branch information
mtdudek committed Oct 12, 2023
1 parent 53f58d4 commit a223069
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 60 deletions.
78 changes: 54 additions & 24 deletions rowhammer_tester/gateware/bist.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,13 +105,15 @@ class BISTModule(Module):
the operation. When the operation is ongoing `ready` will be 0.
"""
def __init__(self, pattern_mem):
self.start = Signal()
self.ready = Signal()
self.count = Signal(32)
self.done = Signal(32)
self.start = Signal()
self.ready = Signal()
self.modulo = Signal()
self.count = Signal(32)
self.done = Signal(32)

self.mem_mask = Signal(32)
self.data_mask = Signal(32)
self.data_mask = Signal(max=pattern_mem.data.depth)
self.data_div = Signal(max=pattern_mem.data.depth)

self.data_port = pattern_mem.data.get_port(mode=READ_FIRST)
self.addr_port = pattern_mem.addr.get_port(mode=READ_FIRST)
Expand All @@ -121,24 +123,32 @@ def add_csrs(self):
self._start = CSR()
self._start.description = 'Write to the register starts the transfer (if ready=1)'
self._ready = CSRStatus(description='Indicates that the transfer is not ongoing')
self._modulo = CSRStorage(description='When set use modulo to calculate DMA transfers address'
' rather than bit masking')
self._count = CSRStorage(size=len(self.count), description='Desired number of DMA transfers')
self._done = CSRStatus(size=len(self.done), description='Number of completed DMA transfers')
self._mem_mask = CSRStorage(
size = len(self.mem_mask),
description = 'DRAM address mask for DMA transfers'
)
self._data_mask = CSRStorage(
size = len(self.mem_mask),
size = len(self.data_mask),
description = 'Pattern memory address mask'
)
self._data_div = CSRStorage(
size = len(self.data_mask),
description = 'Pattern memory address divisior-1'
)

self.comb += [
self.start.eq(self._start.re),
self._ready.status.eq(self.ready),
self.modulo.eq(self._modulo.storage),
self.count.eq(self._count.storage),
self._done.status.eq(self.done),
self.mem_mask.eq(self._mem_mask.storage),
self.data_mask.eq(self._data_mask.storage),
self.data_div.eq(self._data_div.storage),
]


Expand All @@ -158,11 +168,11 @@ def __init__(self, dram_port, pattern_mem, *, rowbits, row_shift):
""".format(common=BISTModule.__doc__))

dma = LiteDRAMDMAWriter(dram_port, fifo_depth=4, fifo_buffered=True)
self.submodules += dma
self.submodules.dma = dma

cmd_counter = Signal(32)
mem_addr = Signal.like(cmd_counter)
dram_addr = Signal.like(dma.sink.address)
mem_addr = Signal.like(self.data_mask)
self.dram_addr = dram_addr = Signal.like(dma.sink.address)

wait_counter = Signal(max=3)

Expand All @@ -177,7 +187,7 @@ def __init__(self, dram_port, pattern_mem, *, rowbits, row_shift):

# DMA data may be inverted using AddressSelector
self.submodules.inverter = RowDataInverter(
addr = dma.sink.address,
addr = dram_addr,
data_in = self.data_port.dat_r,
data_out = dma.sink.data,
rowbits = rowbits,
Expand All @@ -189,11 +199,14 @@ def __init__(self, dram_port, pattern_mem, *, rowbits, row_shift):
self.ready.eq(1),
If(self.start,
NextValue(cmd_counter, 0),
NextValue(mem_addr, 0),
NextState("COMPUTE_MEM_ADDR"),
)
)
fsm.act("COMPUTE_MEM_ADDR",
NextValue(mem_addr, cmd_counter & self.data_mask),
If(~self.modulo,
NextValue(mem_addr, cmd_counter[:len(self.data_mask)] & self.data_mask),
),
If(cmd_counter >= self.count,
NextState("READY")
).Else(
Expand All @@ -205,7 +218,8 @@ def __init__(self, dram_port, pattern_mem, *, rowbits, row_shift):
)
fsm.act("COMPUTE_DRAM_ADDR",
NextValue(wait_counter, 0),
NextValue(dram_addr, self.addr_port.dat_r + (cmd_counter & self.mem_mask)),
NextValue(dram_addr, self.addr_port.dat_r +
(cmd_counter & self.mem_mask)),
NextState("INVERT_COMPUTE"),
)
fsm.act("INVERT_COMPUTE",
Expand All @@ -218,13 +232,24 @@ def __init__(self, dram_port, pattern_mem, *, rowbits, row_shift):
dma.sink.valid.eq(1),
If(dma.sink.ready,
NextValue(cmd_counter, cmd_counter + 1),
If(self.modulo & (mem_addr == self.data_div),
NextValue(mem_addr, 0),
).Elif(self.modulo,
NextValue(mem_addr, mem_addr + 1),
),
NextState("COMPUTE_MEM_ADDR")
)
)

def add_csrs(self):
super().add_csrs()
self.inverter.add_csrs()
self._last_address = CSRStatus(size=32, description='Number of completed DMA transfers')
self.sync += [
If(self.dma.sink.valid & self.dma.sink.ready,
self._last_address.status.eq(self.dram_addr)
),
]


class Reader(BISTModule, AutoCSR, AutoDoc):
Expand Down Expand Up @@ -278,7 +303,7 @@ def __init__(self, dram_port, pattern_mem, *, rowbits, row_shift):

# ----------------- Address FSM -----------------
counter_addr = Signal(32)
mem_addr = Signal.like(counter_addr)
mem_addr = Signal.like(self.data_mask)
dram_addr = Signal.like(dma.sink.address)

self.comb += [
Expand All @@ -292,11 +317,14 @@ def __init__(self, dram_port, pattern_mem, *, rowbits, row_shift):
fsm_addr.act("READY",
If(self.start,
NextValue(counter_addr, 0),
NextValue(mem_addr, 0),
NextState("COMPUTE_MEM_ADDR"),
),
)
fsm_addr.act("COMPUTE_MEM_ADDR",
NextValue(mem_addr, counter_addr & self.data_mask),
If(~self.modulo,
NextValue(mem_addr, counter_addr[:len(self.data_mask)] & self.data_mask),
),
If(counter_addr >= self.count,
NextState("READY"),
).Else(
Expand All @@ -307,7 +335,8 @@ def __init__(self, dram_port, pattern_mem, *, rowbits, row_shift):
NextState("COMPUTE_DRAM_ADDR"),
)
fsm_addr.act("COMPUTE_DRAM_ADDR",
NextValue(dram_addr, self.addr_port.dat_r + (counter_addr & self.mem_mask)),
NextValue(dram_addr, self.addr_port.dat_r +
(counter_addr & self.mem_mask)),
NextState("PUSH_TO_FIFO"),
)
fsm_addr.act("PUSH_TO_FIFO",
Expand All @@ -320,13 +349,18 @@ def __init__(self, dram_port, pattern_mem, *, rowbits, row_shift):
dma.sink.valid.eq(1),
If(dma.sink.ready,
NextValue(counter_addr, counter_addr + 1),
If(self.modulo & (mem_addr == self.data_div),
NextValue(mem_addr, 0),
).Elif(self.modulo,
NextValue(mem_addr, mem_addr + 1),
),
NextState("COMPUTE_MEM_ADDR")
)
)

# ------------- Pattern FSM ----------------
counter_gen = Signal(32)
data_mem_addr = Signal.like(counter_gen)
data_mem_addr = Signal.like(self.data_mask)
wait_counter = Signal(max=3)

# Unmatched memory offsets
Expand All @@ -348,12 +382,15 @@ def __init__(self, dram_port, pattern_mem, *, rowbits, row_shift):
self.ready.eq(1),
If(self.start,
NextValue(counter_gen, 0),
NextValue(data_mem_addr, 0),
NextValue(self.error_count, 0),
NextState("COMPUTE_MEM_ADDR"),
)
)
fsm_pattern.act("COMPUTE_MEM_ADDR",
NextValue(data_mem_addr, counter_gen & self.data_mask),
If(~self.modulo,
NextValue(data_mem_addr, counter_gen & self.data_mask),
),
If(counter_gen >= self.count,
NextState("READY")
).Else(
Expand All @@ -372,13 +409,6 @@ def __init__(self, dram_port, pattern_mem, *, rowbits, row_shift):
),
NextValue(wait_counter, wait_counter + 1),
)
fsm_pattern.act("WAIT", # TODO: we could pipeline the access
If(counter_gen >= self.count,
NextState("READY")
).Else(
NextState("RD_DATA")
)
)
fsm_pattern.act("RD_DATA",
If(dma.source.valid,
# we must now change FSM state in single cycle
Expand Down
11 changes: 7 additions & 4 deletions rowhammer_tester/scripts/hw_rowhammer.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,14 @@ def attack(self, row_tuple, read_count, progress_header=''):

# Do not increment memory address
self.wb.regs.reader_mem_mask.write(0x00000000)
self.wb.regs.reader_data_mask.write(len(row_tuple) - 1)
self.wb.regs.reader_modulo.write(1)
self.wb.regs.reader_data_div.write(len(row_tuple) - 1)
#self.wb.regs.reader_data_mask.write(len(row_tuple) - 1)

# Attacked addresses
memwrite(self.wb, addresses, base=self.wb.mems.writer_pattern_addr.base)
memwrite(self.wb, addresses, base=self.wb.mems.reader_pattern_addr.base)
memwrite(self.wb, ([0xaaaaaaaa]*16),
base=self.wb.mems.reader_pattern_data.base)

# how many
print('read_count: ' + str(int(read_count)))
Expand All @@ -63,11 +66,11 @@ def progress(count):
progress(r_count)
if self.wb.regs.reader_ready.read():
break
else:
time.sleep(10 / 1e3)
time.sleep(10 / 1e3)

progress(self.wb.regs.reader_done.read()) # also clears the value
print()
self.wb.regs.reader_modulo.write(0)

def check_errors(self, row_pattern):
dma_data_width = self.settings.phy.dfi_databits * self.settings.phy.nphases
Expand Down
34 changes: 18 additions & 16 deletions rowhammer_tester/scripts/rowhammer.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,20 +216,7 @@ def no_attack_sleep(self):

print()

def run(self, row_pairs, pattern_generator, read_count, row_progress=16, verify_initial=False):
"""
Main part of the script.
First fills the memory with specified patterns, then optionally checks its integrity.
It disables refreshes if requested.
Next it executes the attack, one row pair at a time.
If refreshes were disabled, it reenables them.
It checks for errors, and if any were found, displays them.
"""

# TODO: need to invert data when writing/reading, make sure Python integer inversion works correctly
if self.data_inversion:
raise NotImplementedError('Currently only HW rowhammer supports data inversion')

def prepare_memory(self):
print('\nPreparing ...')
row_patterns = pattern_generator(self.rows)

Expand All @@ -252,6 +239,23 @@ def run(self, row_pairs, pattern_generator, read_count, row_progress=16, verify_
self.display_errors(errors, read_count)
return


def run(self, row_pairs, pattern_generator, read_count, row_progress=16, verify_initial=False):
"""
Main part of the script.
First fills the memory with specified patterns, then optionally checks its integrity.
It disables refreshes if requested.
Next it executes the attack, one row pair at a time.
If refreshes were disabled, it reenables them.
It checks for errors, and if any were found, displays them.
"""

# TODO: need to invert data when writing/reading, make sure Python integer inversion works correctly
if self.data_inversion:
raise NotImplementedError('Currently only HW rowhammer supports data inversion')

self.prepare_memory()

if self.no_refresh:
print('\nDisabling refresh ...')
self.wb.regs.controller_settings_refresh.write(0)
Expand Down Expand Up @@ -416,8 +420,6 @@ def main(row_hammer_cls):
args.no_refresh = True

if args.hammer_only:
if not args.payload_executor and len(args.hammer_only) != 2:
parser.error("")
row_pairs = [tuple(args.hammer_only)]
elif args.all_rows:
if args.row_pair_distance < 0:
Expand Down
2 changes: 1 addition & 1 deletion rowhammer_tester/scripts/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ def compare(val, ref, fmt, nbytes=4):

def memwrite(wb, data, base=0x40000000, burst=0xff):
for i in range(0, len(data), burst):
wb.write(base + 4 * i, data[i:i + burst])
wb.write(base + 4 * i, data[i:min(i + burst, len(data))])


def memread(wb, n, base=0x40000000, burst=0xff):
Expand Down
Loading

0 comments on commit a223069

Please sign in to comment.