© Harry Broeders.
Deze pagina is bestemd voor studenten van de Haagse Hogeschool - Academie voor Technology, Innovation & Society Delft groep ECNES = minor Embedded Systems.
Het vak "Hardware/Software Codesign with SystemC" richt zich vooral op de eerste stappen in het ESL ontwerpproces: het modeleren van het systeem op systeemniveau met behulp van de modelleringstaal SystemC, het uitvoeren van een "design space exploration" waarin de verschillende implementatiemogelijkheden worden verkend en met elkaar worden vergeleken en de software en hardware taken worden gedefinieerd. Bij dit vak wordt ook een voorbeeld van een hardware design flow gegeven. In het practicum "Hardware/Software Codesign with SystemC" leer je in de eerste opdracht werken met SystemC en ga je in de tweede opdracht een beeldbewerkings-applicatie die geschreven is in C++ optimaliseren voor het DE2-70 ontwikkelbord. Dit kan gedaan worden door de software te optimaliseren maar ook door bepaalde delen van de software in hardware te implementeren.
Beschikbaar matriaal:
Goed
inleidend artikel over SystemC.
sc_module. Channels worden in SystemC geïmplementeerd met
verschillende classes uit de SystemC library afhankelijk van het
abstactieniveau.| Abstractieniveau | modules modelleren: | channels modelleren: | SystemC class(es) voor channel(s) |
|---|---|---|---|
| Gate Level (poort niveau). Dit is het laagste abstractieniveau dat in SystemC beschreven kan worden. Het wordt gebruikt voor het modelleren van hardware. | Het systeem bestaat op dit abstractieniveau uit logische poorten (and, or, inverter, enz). | Elektrische verbinding (metaalspoortje of draadje) | sc_signal<sc_logic>sc_signal<bool> sc_buffer<sc_logic> |
| Register Transfer Level (RTL). Dit abstactieniveau wordt vaak gebruikt voor het modelleren van hardware maar kan ook gebruikt worden om software op een heel laag abstractieniveau (assembler) te beschrijven. | Het systeem bestaat op dit abstractieniveau uit registers, optellers, vermenigvuldigers, schuifregisters, tellers, multiplexers enz. | Elektrische verbinding of groepjes van deze verbindingen (bussen). | sc_signal<...>sc_buffer<...> |
| Behavioural Level. Dit is een abstractieniveau boven RTL waarin de nadruk ligt op het beschrijven van het gedrag (en niet op het beschrijven van de structuur). Dit kan zowel gebruikt worden voor het modelleren van hardware als voor het modelleren van software. | Het systeem bestaat op dit abstractieniveau uit modules boven het RTL niveau. De onderliggende structuur van deze modules wordt niet gemodelleerd. | Op dit niveau kunnen allerlei soorten channels gebruikt worden. De SystemC library bevat een aantal zogenoemde primitive channels maar het is ook mogelijk om zelf andere soorten primitieve channels of om zelf zogenoemde hierarchical channels te implementeren. | sc_signal<...> |
| Transaction Level Modelling. Op dit abstractieniveau wordt de architectuur van de hardware beschreven. | Het systeem bestaat op dit abstractieniveau uit processing elements, geheugens en I/O devices die via één of meerdere bussen met elkaar zijn verbonden. De informatieoverdracht over deze bussen wordt beschreven met behulp van transacties. | SystemC heeft een speciale uitbreidings library voor het modelleren van dit soort modellen: SystemC TLM-2.0.1 | - |
| Function Level: Asynchronous Message Passing. Op dit abstratieniveau wordt het systeem beschreven als een verzameling processen die met elkaar communiceren via ongelimiteerde FIFO's. Dit kan zowel gebruikt worden voor het modelleren van hardware als voor het modelleren van software. | Het systeem bestaat op dit abstractieniveau uit processen. Deze kunnen later in software worden geïmplementeerd als processen/threads/tasks in een operating systeem of als processoren/processing elements in hardware. | FIFO's. Dezen kunnen later in software geïmplementeerd worden als pipes/message channels/TCP verbindingen enz of in hardware als hardware FIFO's. | sc_fifo<...> |
| Function Level: Algorithm Description. Dit is het hoogste abstractieniveau waarin alleen de functionaliteit van de hard- en/of software wordt beschreven. | Op dit abstractieniveau bestaat het hele systeem uit één module. | Er zijn op dit abstractieniveau geen channels | - |
Het gedrag van een module wordt beschreven in SystemC processen. Deze processen
moeten bij de simulatiekern van SystemC geregistreerd worden (call backs).
Bij deze registratie moeten ook de events worden opgegeven waarop het proces
moet reageren. Deze events zijn afkomstig van de channels waarmee de poorten
van de modules verbonden zijn.
Sheet 1 t/m 5 van de TUDelft. In
de traditionele manier van het ontwikkelen van embedded systems werd eerst
de hardware ontworpen en pas daarna de software. De toenemende complexiteit
van embedded systems heeft ertoe geleid dat deze manier van werken niet langer
mogelijk is. Het ontwikkelen van een RTL beschrijving van de hardware neemt
te veel tijd in beslag en het simuleren van een RTL beschrijving van het
systeem (om de software te testen voordat de hardware beschikbaar is) is
veel te langszaam. Daarom willen we het systeem op een hoger abstrcatie niveau
beschrijven. SystemC is een modelleringstaal die dit mogelijk maakt.
SC_MODULE(transmitter)
expandeert niet inclass transmitter: public sc_modulestruct transmitter: public sc_modulestruct bijna exact gelijk aan een
class. Het enige verschil is dat de members in een
class per default private en de members in een
struct per default public zijn.
cout.write(tempC | tempE);Cout.write(tempC | tempE);add mag niet door gebruikers van de class
Adder aangeroepen worden maar wordt door de SystemC simulatiekern
aangeroepen. Het is dus beter om de memberfunctie add private
te declareren zodat het onmogelijk is om deze memberfunctie, per ongeluk,
aan te roepen.tempE = Cin.read() & tempD.read();tempE = Cin.read() & tempD;sc_int maar dat geeft "verrassende"
resultaten. Je kunt beter sc_uint gebruiken. De extra
u zorgt ervoor dat de getallen unsigned zijn. Zie
hier.SC_METHOD en
SC_THREAD. Deze worden later (hoofdstuk 4 van de sheets) nog
uitgebreid behandeld. Voor nu is het voldoende om te weten dat een als
SC_MODULE geregistreerde memberfunctie telkens opnieuw, door
de SystemC simulatiekern, wordt aangeroepen als een event optreedt dat in
de bijbehorende sensitivity list is gespecificeerd. Het uitvoeren van deze
memberfunctie kan niet onderbroken worden door de SystemC simulatiekern.
Een als SC_THREAD geregistreerde memberfunctie daarintegen wordt
slechts één maal, door de SystemC simulatiekern, aangeroepen.
In deze memberfunctie is het mogelijk om de uitvoering tijdelijk te onderbreken
(suspend) door de simulatiekern aan te roepen via de wait functie.
Er bestaan diverse overloaded versies van deze functie waarmee je op
verschillende soorten events kunt wachten. De memberfunctie die
wait heeft aangeroepen wordt weer vervolgd (resume) als het
event, waarop gewacht wordt, is opgetreden. In de voorbeelden wordt dit gebruikt
om een bepaalde tijd te wachten.Error: (E115) sc_signal<T> cannot have more than one driver: signal `RippleCounter2Bits.signal_3' (sc_signal) first driver `RippleCounter2Bits.DFF1.port_2' (sc_out) second driver `RippleCounter2Bits.port_1' (sc_out) In file: c:\systemc-2.2.0\src\sysc\communication\sc_signal.cpp:137
Dit klopt ook want de uitgang bit1 van de
RippleCounter2Bits en de uitgang Q van de subcomponent
dff1 zijn met elkaar verbonden via het signaal
Q1_bit1. Twee uitgangen met elkaar verbinden leidt tot
kortsluitingen en is om die reden verboden. De uitgang Q van
de subcomponent dff1 kan wel rechtstreeks worden verbonden met
de uitgang bit1 van de "omhullende" module
RippleCounter2Bits.
dff1.Q(bit1); // in plaats van bit1(Q1_bit1); en verwijder
Q1_bit1.
Maar nu komt een ander probleem aan het licht. De uitgang Q van subcomponent
dff0 moet verbonden worden met uitgang bit0 van
de omhullende module en óók met de clk ingang
van de subcomponent dff0 (via signaal Q0_clk1).
Maar dat werkt niet want de uitgang Q mag maar 1 keer gebonden worden. Dit
kun je oplossen door de clk ingang van de subcomponent
dff0 rechtstreeks te binden met de uitgang bit0
van de omhullende module. Het signaal Q0_clk1 is dan
overbodig.
Nu gaat het bijna goed! Alleen reageert de teller nog niet goed bij de eerste
opgaande flank van bit0. Dit komt omdat de Q_bar
uitgang van dff1 dan nog gelijk is aan de Q uitgang
van dff1. Een situatie die, nadat het process van
dff1 éénmaal uitgevoerd is als de
enable hoog is, nooit kan optreden omdat Q_bar
dan altijd het inverse is van Q. Dit laatste probleem kan opgelost
worden door een reset signaal toe te voegen. De gecorrigeerde
code kun je
hier vinden.
Het voorbeeld op pagina 52 geeft een compilatiefout omdat het haakje sluiten
van de buitenste if ontbreekt. Over dit model valt het een en
ander op te merken:
RippleCounter2Bits) waarbij de counter aftelt.
reset signaal bij
een positieve flank op de clock als de counter enabled is. Het is gebruikelijker
om een reset signaal asynchroon te maken (onafhankelijk van
de clock) en onafhankelijk van het enable signaal.
#define om de waarde van N te
definiëren tijdens compile time is niet handig! Probeer maar eens twee
counters van het type counterNbit te gebruiken met 2 verschillende
waarden van N. Het is beter om counterNbit als
template class te definiëren zodat we telkens als we een object van
het type counterNbit aanmaken de gewenste waarde van
N als template parameter kunnen meegeven. De aangepaste code
kun je hier
vinden.
Sheet 53 laat zien hoe je een module kunt maken die configureerbaar is tijdens
run time via de constructor. Je kunt in dit geval de macro
SC_CTOR niet gebruiken maar je moet gebruik maken van de macro
SC_HAS_PROCESS. Bedenk dat je een module configureerbaar kunt
maken tijdens compile time door er een template class van te maken.
Zie sheet 16 en 17 voor uitleg over de SC_HAS_PROCESS macro.
Sheet 53 bevat een klein foutje:
sc_in<bool> reset. enable;
De . moet natuurlijk een , zijn.
sc_in<bool> reset, enable;
Sheet 53 bevat ook een logische fout. De waarde van de member variabele count
wordt nooit naar de output poort geschreven! Voor de wait() moet de volgende
regel worden toegevoegd:
out.write(count);
Ook is het logischer om het unsigned type sc_uint<8> in
plaats van het signed type sc_int<8>. De membervariabele
dir kan beter in de initializatielijst geïnitialiseerd
worden. Tot slot vind ik de naam dir niet zo duidelijk,
count_down is beter.
Sheet 55 bevat een klein foutje. De laatste notify sheduled een event na
20 femptoseconden (en niet na 1 zoals in het commentaar staat). Deze zelfde
fout staat ook op pagina 78 van het SystemC boek.
Sheet 60 maakt gebruik van sensitive_pos. Dit is deprecated
in de SystemC standaard. De standaard zegt: "Classes
sc_sensitive_pos and sc_sensitive_neg and the
corresponding data members sensitive_pos and
sensitive_neg of class sc_module are deprecated,
use the event finders pos and neg instead". Dus:
sensitive_pos << clock;
kan beter vervangen worden door:
sensitive << clock.pos();
Op sheet 64 staat een fout.
e_filled.notify(SC_ZERO_TIME);
moet zijn:
e_tank_filled.notify(SC_ZERO_TIME);
De complete code van dit voorbeeld kun je
hier vinden.
SC_HAS_PROCESS.
Opdrachten:
Resultaten practicum: res_HM-ES-pr1.pdf.
Resultaten theorie:
res_HM-ES-sc1.pdf.
Resultaten theorie herkansing:
res_HM-ES-sc1_herk.pdf.