Quando puoi misurare ciò di cui stai parlando, ed esprimerlo in numeri, tu conosci qualcosa su di esso; ma quando non puoi misurarlo, quando non puoi esprimerlo in numeri, la tua conoscenza è scarsa e insoddisfacente:
può essere l’inizio della conoscenza, ma nei tuoi pensieri, sei avanzato poco sulla via della scienza.
– William Thomson, Lord Kelvin (1824-1907)
Una volta realizzata la descrizione in Python del nostro Multiplexer , dobbiamo testare se funziona o meno. Infatti non è sufficiente descriverne il comportamento interno (architecture) e l’interfaccia esterna (design entity) perché il solo modo per verificare che funzioni è di simularlo. In questo articolo avevamo già visto come farlo con ISim ora però non ci resta che farlo in Python sempre con il modulo MyHDL e MyHDL Peek che ci permette la visualizzazione su un Notebook Jupyter dell’andamento dei segnali attraverso le forme d’onda ottenute dalla simulazione.
from myhdlpeek import Peeker
from random import randrange
from myhdl import *
@block
def mux(s, o, I0, I1):
"""
MUX 2:1
I0,I1,s: inputs
O: output
"""
@always_comb
def logic():
if s == 0: # se s=0 in output avremo I0
o.next = I0
else:
o.next = I1 #se = 1 in output avremo I1
return logic
Tech Stack
[1] Installazione del modulo open source MyHDL
[2] Installazione MyHDL Peek
[3] Utilizzo di Python 3.5 installato attraverso Anaconda per Windows
[4] Utilizzo di un notebook Jupyter
Installazione MyHDL Peek
MyHDL Peek è un modulo Python sviluppato da Dave Vandenbout che ci permette di visualizzare il diagramma temporale dei segnali su un notebook Jupyter. Apportare modifiche alla nostra descrizione in Python con MyHDL e visualizzare immediatamente il risultato.
Per installare la versione 0.0.5 di myhdlpeek ci basta digitare:
pip install myhdlpeek
Testbench
La simulazione è una procedura di testing utilizzata per garantire che il circuito da sintetizzare implementi il comportamento previsto.
La procedura è composta dall’istanziazione del modulo da testare (UUT, unity under test) e dagli stimoli da applicare alla UUT.
Infine osserviamo la risposta dell’UUT visualizzando la waveform dell’uscita utilizzando MyHDL Peek.
def testBench():
# inizializzazione dei valori
o, I0,I1 = [Signal(intbv(0)) for i in range(3)] # Integer per i segnali in entrata
s = Signal(bool(0)) # Binary signal per il selettore
#istanziazione del modulo da testare
uut = mux(s, o, I0, I1)
Peeker.clear()
Peeker(I0, 'I0')
Peeker(I1, 'I1')
Peeker(o, 'O')
Peeker(s, 'select')
@instance
def stimulus():
print("t s I0 I1 o")
for i in range(8): # 8 volte
I0.next, I1.next = randrange(8), randrange(8)
s.next =randrange(2)
yield delay(2) #utilizzato alla pari di wait
print("%d %s %s %s %s" % (now(), s, I0, I1,o))
return (uut, stimulus)
Inizializzazione della simulazione
Dobbiamo assegnare ai segnali in input dei valori iniziali in base al loro tipo:
– tre ingressi di tipo integer.
– uscita di tipo booleano.
o, I0,I1 = [Signal(intbv(0)) for i in range(3)] # Integer per i segnali in entrata
s = Signal(bool(0)) # Binary signal per il selettore
Generazione degli stimoli
Quando abbiamo testato il MUX con ISim abbiamo scritto all’interno del process body una serie di assegnamenti tra segnali e di istruzioni wait per fornire gli stimoli:
-- Stimulus process
stim_proc: process
begin
I0 <= '1'; -- assegnamenti
I1 <= '0';
s <= '0';
wait for 20 ns;
s <= '1';
wait for 20 ns;
I0 <= '0';
I1 <= '1';
s <= '0';
wait for 20 ns;
s <= '1';
wait for 20 ns;
end process;
Allo stesso modo con MyHDL creeremo un generatore stimulus() per poi assegnare una serie di valori casuali ai nostri segnali in ingresso:
@instance
def stimulus():
print("t s I0 I1 o")
for i in range(8): # 8 volte
I0.next, I1.next = randrange(8), randrange(8)
s.next =randrange(2)
yield delay(2) #utilizzato alla pari di wait
print("%d %s %s %s %s" % (now(), s, I0, I1,o))
Per farlo utilizzeremo semplicemente il metodo randrange() che restituisce un elemento casuale da range(start,stop,step). Ricordandoci che il selettore è a 1 bit e che può quindi acquisire solo due valori randrange(2)
L’istruzione yield in MyHDL viene impiegata allo stesso modo che wait in VHDL per sospendere l’esecuzione del generatore specificando la condizione che deve verificarsi perché venga ripreso. In questo caso un ritardo della propagazione del segnale con delay().
Simulation
# Simulate the uut, stimulus e peekers.
sim = Simulation(uut, stimulus, *Peeker.instances()).run()
Output waveform
Per un componente di bassa complessità come un Multiplexer per capire se si comporta in modo corretto possiamo analizzare la waveform prodotta dalla simulazione. Innanzitutto dobbiamo associare ad ogni segnale di I/O che dobbiamo monitorare un Peeker:
Peeker.clear() # Clear any existing Peekers.
Peeker(I0, 'I0')
Peeker(I1, 'I1')
Peeker(o, 'O')
Peeker(s, 'select')
Dal nostro taccuino Jupyter MyHDL Peek ci permette di visualizzare i risultati della simulazione con una comoda tabella:
signals = ('select I0 I1 O')
Peeker.to_html_table(signals)
Notiamo subito che il selettore assume come valori 1 o 0 e che e hanno valori casuali.
Dal comportamento del Multiplexer sappiamo che se il selettore assume S=0 in uscita o avremo il valore di e viceversa se l’uscita sarà .
Per esempio all’istante t=2 con S=0 abbiamo come valore in uscita quello di .
Prendiamo con in uscita abbiamo come ci aspettavamo.
Ora non ci resta che controllare il diagramma temporale della simulazione
signals = ('select I0 I1 O')
Peeker.to_wavedrom(signals, start_time=0, stop_time=14, tock=True,title='Output waveform' )
Codice completo
from myhdlpeek import Peeker
from random import randrange
from myhdl import *
@block
def mux(s, o, I0, I1):
"""
MUX 2:1
I0,I1,s inputs
O output
"""
@always_comb
def logic():
if s == 0: # se s=0 in output avremo I0
o.next = I0
else:
o.next = I1 #se = 1 in output avremo I1
return logic
def testBench():
# inizializzazione
o, I0,I1 = [Signal(intbv(0)) for i in range(3)] # Integer per i segnali in entrata
s = Signal(bool(0)) # Binary signal per il selettore
uut = mux(s, o, I0, I1)
Peeker.clear()
Peeker(I0, 'I0')
Peeker(I1, 'I1')
Peeker(o, 'O')
Peeker(s, 'select')
@instance
def stimulus():
print("t s I0 I1 o")
for i in range(8): # 8 volte
I0.next, I1.next = randrange(8), randrange(8)
s.next =randrange(2)
yield delay(2) #utilizzato alla pari di wait
print("%d %s %s %s %s" % (now(), s, I0, I1,o))
return (uut, stimulus)
uut, stimulus = testBench()
# Simulate mux,uut e Peekers
sim = Simulation(uut, stimulus, *Peeker.instances()).run()
# Visualizzabile solo con taccuino Jupyter
Peeker.to_wavedrom()
Bibliografia
Sitografia
MyHDL:documentazione
[1] Jan Decaluwe, MyHDL documentation, Release 1.0dev
[2] Dave Vandenbout, MyHDL Peek documentation, Release 0.0.5