\title{Memories in myHDL} \author{Steven K Armour} \maketitle
In [1]:
from myhdl import *
from myhdlpeek import Peeker
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
from sympy import *
init_printing()
import random
#https://github.com/jrjohansson/version_information
%load_ext version_information
%version_information myhdl, myhdlpeek, numpy, pandas, matplotlib, sympy, random
Out[1]:
In [2]:
#helper functions to read in the .v and .vhd generated files into python
def VerilogTextReader(loc, printresult=True):
with open(f'{loc}.v', 'r') as vText:
VerilogText=vText.read()
if printresult:
print(f'***Verilog modual from {loc}.v***\n\n', VerilogText)
return VerilogText
def VHDLTextReader(loc, printresult=True):
with open(f'{loc}.vhd', 'r') as vText:
VerilogText=vText.read()
if printresult:
print(f'***VHDL modual from {loc}.vhd***\n\n', VerilogText)
return VerilogText
RTL and Implimentation Schamatics are from Xilinx Vivado 2016.1
ROM is a memory structure that holds static information that can only be read from. In other words, these are hard-coded instruction memory. That should never change. Furthermore, this data is held in a sort of array; for example, we can think of a python tuple as a sort of read-only memory since the content of a tuple is static and we use array indexing to access a certain portions of the memory.
In [3]:
#use type casting on list genrator to store 0-9 in 8bit binary
TupleROM=tuple([bin(i, 8) for i in range(10)])
TupleROM
Out[3]:
In [4]:
f'accesss location 6: {TupleROM[6]}, read contents of location 6 to dec:{int(TupleROM[6], 2)}'
Out[4]:
And if we try writing to the tuple we will get an error
In [5]:
#TupleROM[6]=bin(16,2)
So to start off with the Random in RAM does not mean Random in a proplositc sence. It refares to Random as in you can randomly access any part of the data array opposed to the now specility sequantil only memory wich are typicly made with a counter or stat machine to sequance that acation
in HDL ROM the data is stored a form of a D flip flop that are structerd in a sort of two diminal array where one axis is the address and the other is the content and we use a mux to contorl wich address "row" we are trying to read. There fore we have two signals: address and content. Where the address contorls the mux.
In [6]:
@block
def ROMLoaded(addr, dout):
"""
A ROM laoded with data already incoded in the structer
insted of using myHDL inchanced parmter loading
I/O:
addr(Signal>4): addres; range is from 0-3
dout(Signal>4): data at each address
"""
@always_comb
def readAction():
if addr==0:
dout.next=3
elif addr==1:
dout.next=2
elif addr==2:
dout.next=1
elif addr==3:
dout.next=0
return instances()
In [7]:
Peeker.clear()
addr=Signal(intbv(0)[4:]); Peeker(addr, 'addr')
dout=Signal(intbv(0)[4:]); Peeker(dout, 'dout')
DUT=ROMLoaded(addr, dout)
def ROMLoaded_TB():
"""Python Only Testbench for `ROMLoaded`"""
@instance
def stimules():
for i in range(3+1):
addr.next=i
yield delay(1)
raise StopSimulation()
return instances()
sim = Simulation(DUT, ROMLoaded_TB(), *Peeker.instances()).run()
In [8]:
Peeker.to_wavedrom()
In [9]:
Peeker.to_dataframe()
Out[9]:
In [10]:
DUT.convert()
VerilogTextReader('ROMLoaded');
ROMLoaded RTL
In [11]:
@block
def ROMLoaded_TBV():
"""Verilog Only Testbench for `ROMLoaded`"""
clk = Signal(bool(0))
addr=Signal(intbv(0)[4:])
dout=Signal(intbv(0)[4:])
DUT=ROMLoaded(addr, dout)
@instance
def clk_signal():
while True:
clk.next = not clk
yield delay(10)
@instance
def stimules():
for i in range(3+1):
addr.next=i
#yield delay(1)
yield clk.posedge
raise StopSimulation
@always(clk.posedge)
def print_data():
print(addr, dout)
return instances()
#create instaince of TB
TB=ROMLoaded_TBV()
#convert to verilog with reintilzed values
TB.convert(hdl="Verilog", initial_values=True)
#readback the testbench results
VerilogTextReader('ROMLoaded_TBV');
With myHDL we can dynamicaly load the contents that will be hard coded in the convertion to verilog/VHDL wich is an ammazing benfict for devlopment as is sean here
In [12]:
@block
def ROMParmLoad(addr, dout, CONTENT):
"""
A ROM laoded with data from CONTENT input tuple
I/O:
addr(Signal>4): addres; range is from 0-3
dout(Signal>4): data at each address
Parm:
CONTENT: tuple size 4 with contende must be no larger then 4bit
"""
@always_comb
def readAction():
dout.next=CONTENT[int(addr)]
return instances()
In [13]:
Peeker.clear()
addr=Signal(intbv(0)[4:]); Peeker(addr, 'addr')
dout=Signal(intbv(0)[4:]); Peeker(dout, 'dout')
CONTENT=tuple([i for i in range(4)][::-1])
DUT=ROMParmLoad(addr, dout, CONTENT)
def ROMParmLoad_TB():
"""Python Only Testbench for `ROMParmLoad`"""
@instance
def stimules():
for i in range(3+1):
addr.next=i
yield delay(1)
raise StopSimulation()
return instances()
sim = Simulation(DUT, ROMParmLoad_TB(), *Peeker.instances()).run()
In [14]:
Peeker.to_wavedrom()
In [15]:
Peeker.to_dataframe()
Out[15]:
In [16]:
DUT.convert()
VerilogTextReader('ROMParmLoad');
ROMParmLoad RTL
In [17]:
@block
def ROMParmLoad_TBV():
"""Verilog Only Testbench for `ROMParmLoad`"""
clk=Signal(bool(0))
addr=Signal(intbv(0)[4:])
dout=Signal(intbv(0)[4:])
CONTENT=tuple([i for i in range(4)][::-1])
DUT=ROMParmLoad(addr, dout, CONTENT)
@instance
def clk_signal():
while True:
clk.next = not clk
yield delay(1)
@instance
def stimules():
for i in range(3+1):
addr.next=i
yield clk.posedge
raise StopSimulation
@always(clk.posedge)
def print_data():
print(addr, dout)
return instances()
#create instaince of TB
TB=ROMParmLoad_TBV()
#convert to verilog with reintilzed values
TB.convert(hdl="Verilog", initial_values=True)
#readback the testbench results
VerilogTextReader('ROMParmLoad_TBV');
we can also create rom that insted of being asynchronous is synchronous
In [18]:
@block
def ROMParmLoadSync(addr, dout, clk, rst, CONTENT):
"""
A ROM laoded with data from CONTENT input tuple
I/O:
addr(Signal>4): addres; range is from 0-3
dout(Signal>4): data at each address
clk (bool): clock feed
rst (bool): reset
Parm:
CONTENT: tuple size 4 with contende must be no larger then 4bit
"""
@always(clk.posedge)
def readAction():
if rst:
dout.next=0
else:
dout.next=CONTENT[int(addr)]
return instances()
In [19]:
Peeker.clear()
addr=Signal(intbv(0)[4:]); Peeker(addr, 'addr')
dout=Signal(intbv(0)[4:]); Peeker(dout, 'dout')
clk=Signal(bool(0)); Peeker(clk, 'clk')
rst=Signal(bool(0)); Peeker(rst, 'rst')
CONTENT=tuple([i for i in range(4)][::-1])
DUT=ROMParmLoadSync(addr, dout, clk, rst, CONTENT)
def ROMParmLoadSync_TB():
"""Python Only Testbench for `ROMParmLoadSync`"""
@always(delay(1))
def ClkGen():
clk.next=not clk
@instance
def stimules():
for i in range(3+1):
yield clk.posedge
addr.next=i
for i in range(4):
yield clk.posedge
rst.next=1
addr.next=i
raise StopSimulation()
return instances()
sim = Simulation(DUT, ROMParmLoadSync_TB(), *Peeker.instances()).run()
In [20]:
Peeker.to_wavedrom()
In [21]:
ROMData=Peeker.to_dataframe()
#keep only clock high
ROMData=ROMData[ROMData['clk']==1]
ROMData.drop(columns='clk', inplace=True)
ROMData.reset_index(drop=True, inplace=True)
ROMData
Out[21]:
In [22]:
DUT.convert()
VerilogTextReader('ROMParmLoadSync');
ROMParmLoadSync RTL
In [23]:
@block
def ROMParmLoadSync_TBV():
"""Python Only Testbench for `ROMParmLoadSync`"""
addr=Signal(intbv(0)[4:])
dout=Signal(intbv(0)[4:])
clk=Signal(bool(0))
rst=Signal(bool(0))
CONTENT=tuple([i for i in range(4)][::-1])
DUT=ROMParmLoadSync(addr, dout, clk, rst, CONTENT)
@instance
def clk_signal():
while True:
clk.next = not clk
yield delay(1)
@instance
def stimules():
for i in range(3+1):
yield clk.posedge
addr.next=i
for i in range(4):
yield clk.posedge
rst.next=1
addr.next=i
raise StopSimulation
@always(clk.posedge)
def print_data():
print(addr, dout, rst)
return instances()
#create instaince of TB
TB=ROMParmLoadSync_TBV()
#convert to verilog with reintilzed values
TB.convert(hdl="Verilog", initial_values=True)
#readback the testbench results
VerilogTextReader('ROMParmLoadSync_TBV');
In [30]:
@block
def SeqROMEx(clk, rst, dout):
"""
Seq Read Only Memory Ex
I/O:
clk (bool): clock
rst (bool): rst on counter
dout (signal >4): data out
"""
Count=Signal(intbv(0)[3:])
@always(clk.posedge)
def counter():
if rst:
Count.next=0
elif Count==3:
Count.next=0
else:
Count.next=Count+1
@always(clk.posedge)
def Memory():
if Count==0:
dout.next=3
elif Count==1:
dout.next=2
elif Count==2:
dout.next=1
elif Count==3:
dout.next=0
return instances()
In [31]:
Peeker.clear()
dout=Signal(intbv(0)[4:]); Peeker(dout, 'dout')
clk=Signal(bool(0)); Peeker(clk, 'clk')
rst=Signal(bool(0)); Peeker(rst, 'rst')
DUT=SeqROMEx(clk, rst, dout)
def SeqROMEx_TB():
"""Python Only Testbench for `SeqROMEx`"""
@always(delay(1))
def ClkGen():
clk.next=not clk
@instance
def stimules():
for i in range(5+1):
yield clk.posedge
for i in range(4):
yield clk.posedge
rst.next=1
raise StopSimulation()
return instances()
sim = Simulation(DUT, SeqROMEx_TB(), *Peeker.instances()).run()
In [32]:
Peeker.to_wavedrom()
In [33]:
SROMData=Peeker.to_dataframe()
#keep only clock high
SROMData=SROMData[SROMData['clk']==1]
SROMData.drop(columns='clk', inplace=True)
SROMData.reset_index(drop=True, inplace=True)
SROMData
Out[33]:
In [34]:
DUT.convert()
VerilogTextReader('SeqROMEx');
SeqROMEx RTL
In [45]:
@block
def SeqROMEx_TBV():
"""Verilog Only Testbench for `SeqROMEx`"""
dout=Signal(intbv(0)[4:])
clk=Signal(bool(0))
rst=Signal(bool(0))
DUT=SeqROMEx(clk, rst, dout)
@instance
def clk_signal():
while True:
clk.next = not clk
yield delay(1)
@instance
def stimules():
for i in range(5+1):
yield clk.posedge
for i in range(4):
yield clk.posedge
rst.next=1
raise StopSimulation()
@always(clk.posedge)
def print_data():
print(clk, rst, dout)
return instances()
#create instaince of TB
TB=SeqROMEx_TBV()
#convert to verilog with reintilzed values
TB.convert(hdl="Verilog", initial_values=True)
#readback the testbench results
VerilogTextReader('SeqROMEx_TBV');
In [36]:
@block
def RAMConcur(addr, din, writeE, dout, clk):
"""
Random access read write memeory
I/O:
addr(signal>4): the memory cell arrdress
din (signal>4): data to write into memeory
writeE (bool): write enable contorl; false is read only
dout (signal>4): the data out
clk (bool): clock
Note:
this is only a 4 byte memory
"""
#create the memeory list (1D array)
memory=[Signal(intbv(0)[4:]) for i in range(4)]
@always(clk.posedge)
def writeAction():
if writeE:
memory[addr].next=din
@always_comb
def readAction():
dout.next=memory[addr]
return instances()
In [37]:
Peeker.clear()
addr=Signal(intbv(0)[4:]); Peeker(addr, 'addr')
din=Signal(intbv(0)[4:]); Peeker(din, 'din')
writeE=Signal(bool(0)); Peeker(writeE, 'writeE')
dout=Signal(intbv(0)[4:]); Peeker(dout, 'dout')
clk=Signal(bool(0)); Peeker(clk, 'clk')
CONTENT=tuple([i for i in range(4)][::-1])
DUT=RAMConcur(addr, din, writeE, dout, clk)
def RAMConcur_TB():
"""Python Only Testbench for `RAMConcur`"""
@always(delay(1))
def ClkGen():
clk.next=not clk
@instance
def stimules():
# do nothing
for i in range(1):
yield clk.posedge
#write memory
for i in range(4):
yield clk.posedge
writeE.next=True
addr.next=i
din.next=CONTENT[i]
#do nothing
for i in range(1):
yield clk.posedge
writeE.next=False
#read memory
for i in range(4):
yield clk.posedge
addr.next=i
# rewrite memory
for i in range(4):
yield clk.posedge
writeE.next=True
addr.next=i
din.next=CONTENT[-i]
#do nothing
for i in range(1):
yield clk.posedge
writeE.next=False
#read memory
for i in range(4):
yield clk.posedge
addr.next=i
raise StopSimulation()
return instances()
sim = Simulation(DUT, RAMConcur_TB(), *Peeker.instances()).run()
In [38]:
Peeker.to_wavedrom()
In [39]:
RAMData=Peeker.to_dataframe()
RAMData=RAMData[RAMData['clk']==1]
RAMData.drop(columns='clk', inplace=True)
RAMData.reset_index(drop=True, inplace=True)
RAMData
Out[39]:
In [40]:
RAMData[RAMData['writeE']==1]
Out[40]:
In [41]:
RAMData[RAMData['writeE']==0]
Out[41]:
In [42]:
DUT.convert()
VerilogTextReader('RAMConcur');
RAMConcur RTL
In [47]:
@block
def RAMConcur_TBV():
"""Verilog Only Testbench for `RAMConcur`"""
addr=Signal(intbv(0)[4:])
din=Signal(intbv(0)[4:])
writeE=Signal(bool(0))
dout=Signal(intbv(0)[4:])
clk=Signal(bool(0))
CONTENT=tuple([i for i in range(4)][::-1])
DUT=RAMConcur(addr, din, writeE, dout, clk)
@instance
def clk_signal():
while True:
clk.next = not clk
yield delay(1)
@instance
def stimules():
# do nothing
for i in range(1):
yield clk.posedge
#write memory
for i in range(4):
yield clk.posedge
writeE.next=True
addr.next=i
din.next=CONTENT[i]
#do nothing
for i in range(1):
yield clk.posedge
writeE.next=False
#read memory
for i in range(4):
yield clk.posedge
addr.next=i
# rewrite memory
for i in range(4):
yield clk.posedge
writeE.next=True
addr.next=i
din.next=CONTENT[-i]
#do nothing
for i in range(1):
yield clk.posedge
writeE.next=False
#read memory
for i in range(4):
yield clk.posedge
addr.next=i
raise StopSimulation()
@always(clk.posedge)
def print_data():
print(addr, din, writeE, dout, clk)
return instances()
#create instaince of TB
TB=RAMConcur_TBV()
#convert to verilog with reintilzed values
TB.convert(hdl="Verilog", initial_values=True)
#readback the testbench results
VerilogTextReader('RAMConcur_TBV');
In [ ]: