Moin,
ich habe aktuell folgendes Problem: Ich verwende in meinem Design ein
selbstgeschriebenes ALU-Modul, welches ich sowohl für die Berechnung von
Addition usw. als auch für Vergleiche benutze. Normale Operationen
sollen mit 32-Bit Breite stattfinden, Vergleiche mit 33 Bits, um das
Carry aufzufangen. Es gibt dazu ein Steuerbit, welches zwischen Sign-
und Zero-extension umschaltet.
Das Ganze sieht also etwa so aus:
reg[32:0] aluExtendedResult; // wird aus den extended-Operanden berechnet
10
...
11
wire[31:0] aluResult = aluExtendedResult[31:0];
12
...
Bei den nicht-vergleichenden Berechnungen wird aluSignedComparison auf x
gesetzt, um der Synthese ihre Freiheit zu lassen. Die verwendeten
Operatoren sind alle dergestalt, dass das 33-te Bit keinen Einfluss auf
die unteren 32 Bits hat und damit das Ergebnis nicht verfälscht...
sollte man zumindest meinen.
Nun versuche ich in der Simulation eine ADD-Operation durchzuführen.
Dabei wird aluSignedComparison auf x gesetzt; einer der Operanden ist
negativ. Der aluExtendedRightOperand hat folglich ein x im obersten Bit,
die anderen Bits erscheinen wie gewünscht. Der linke Operand ist
positiv, daher 0 statt x im 33. Bit.
Problem: Die Addition der beiden scheint 33 mal x zu ergeben. Und an der
Stelle stehe ich auf dem Schlauch, was da genau schiefgeht. Es ist mit
klar, dass das 33. Bit des Ergebnisses undefiniert ist, da 0+x=x. Es ist
mir aber völlig schleierhaft, wie die unteren 32 Bits des Ergebnisses,
deren Operanden ja definiert sind, x werden können. Auch ein evtl. Carry
wird ja nicht nach unten weitergegeben, nur nach oben.
Ich benutze Icarus Verilog zum simulieren, falls das einen Unterschied
macht. Der komplette Code dieses Modulteils hängt unten an.
Danke schonmal für jeden Denkanstoß!
1
/**
2
* This file defines the ALU. The operands and operation to compute must
3
* be stored in the corresponding registers. The outputs are asynchronously
4
* computed.
5
*
6
* NOTE: In the current CPU design, the operands are not actually stored
7
* in registers, but are asynchronously computed from the outputs of the
8
* register file and the immediate value. However, the Verilog "reg"
9
* construct is still used and allows to specify the inputs of the ALU
10
* in a simple way.
11
*/
12
13
/**
14
* The operation to compute must be stored in this register.
15
*/
16
reg[2:0] aluOperationRegister;
17
18
/**
19
* The left operand must be stored in this register.
20
*/
21
reg[31:0] aluLeftOperand;
22
23
/**
24
* The right operand must be stored in this register.
25
*/
26
reg[31:0] aluRightOperand;
27
28
/**
29
* This result is computed asynchronously from the operands
30
* and operation.
31
* Note that this is a "reg" in Verilog terms, but not an actual register.
32
*/
33
wire[31:0] aluResult = aluExtendedResult[31:0];
34
35
/**
36
* This result is computed asynchronously from the operands
37
* and operation. It is 1 exactly if the operands are equal.
Bist auch ganz sicher, dass "case (aluOperationRegister)" nicht im
default Zweig landet?
Es würde natürlich helfen, den ganzen Code zu sehen...
--
Marcus
Für diejenigen, die mal auf dasselbe Problem stoßen: Ich habe ein
Workaround gefunden. Es ist möglich, für Simulationszwecke aus einem x
einen Definierten Wert zu machen (bis jetzt nicht sicher ob 1 oder 0,
aber man sollte das ja auch nur dann machen, wenn 0/1 keine Rolle
spielt):
1
wire undefinedSignal = ...;
2
reg definedSignal;
3
always @(*) begin
4
if (undefinedSignal) begin
5
definedSignal <= 1'b1;
6
end else begin
7
definedSignal <= 1'b0;
8
end
9
end
Dieser Code sieht so aus, als würde er gar nichts machen -- was bei der
Synthese auch stimmt -- aber für die Simulation liegt jetzt ein
definierter Wert vor. Tip: Man sollte diesen Wert nur da verwenden, wo
es nötig ist, so wie in meinem Fall. Ansonsten verschleiert man ggf. an
ungewünschter Stelle, dass ein Signalwert undefiniert ist.
@Marcus:
Danke für die Antwort.
> Bist auch ganz sicher, dass "case (aluOperationRegister)" nicht im> default Zweig landet?
Ja, denn der Wert des aluOperationRegister ist gleich
`ALU_OPERATION_ADD, und das "Definiert-Machen" des Erweiterungsbits löst
das Problem.
> Es würde natürlich helfen, den ganzen Code zu sehen...
Der wäre viele Seiten lang. Ich habe den Code schon auf das wesentliche
Reduziert -- hoffte ich zumindest.
Allerdings verunsichert es mich etwas, dass du das Verhalten nicht
allein mit dem von mir geposteten Code reproduzieren kannst. Das würde
dann ja bedeuten, dass das seltsame Verhalten bzgl. "x" bei der Addition
bei dir nicht auftritt. Welchen Simulator benutzt du denn?
Morin schrieb:> Es ist möglich, für Simulationszwecke aus einem x einen Definierten> Wert zu machen (bis jetzt nicht sicher ob 1 oder 0, aber man sollte> das ja auch nur dann machen, wenn 0/1 keine Rolle spielt):
Der boolsche Wert von 1'bx ist "false". Aber wenn Du das x in einen
definierten Wert wandelst, solltest Du dann nicht besser von vorneherein
einen definierten Wert verwenden?
> Der wäre viele Seiten lang. Ich habe den Code schon auf das wesentliche> Reduziert -- hoffte ich zumindest.
Nun ja. Das wesentliche wäre gewesen, eine Datei zu erzeugen die den
Fehler isoliert und von anderen ohne weiteren Aufwand durch Compiler
und Simulator geschickt werden kann.
> Allerdings verunsichert es mich etwas, dass du das Verhalten nicht> allein mit dem von mir geposteten Code reproduzieren kannst. Das würde> dann ja bedeuten, dass das seltsame Verhalten bzgl. "x" bei der Addition> bei dir nicht auftritt.
Das habe ich nicht gesagt. Ich habe schlicht keine Zeit gehabt, die
Code Schnipsel in ein funktionsfähiges Design zu verwandeln und das
mal zu testen.
> Welchen Simulator benutzt du denn?
Questa. Aber meine Lizenz ist zu allem Übel gerade abgelaufen und ich
habe noch keine neue bekommen.
--
Marcus
IEEE 1364-2005, sec. 5.1.5:
"For the arithmetic operators, if any operand bit value is the unknown
value x or the high-impedance value z, then the entire result value
shall be x."
Viele Grüße
Marcus
> Der boolsche Wert von 1'bx ist "false". Aber wenn Du das x in einen> definierten Wert wandelst, solltest Du dann nicht besser von vorneherein> einen definierten Wert verwenden?
Dann würde ich die Optimierung bei der Synthese unnötig einschränken.
> Nun ja. Das wesentliche wäre gewesen, eine Datei zu erzeugen die den> Fehler isoliert und von anderen ohne weiteren Aufwand durch Compiler> und Simulator geschickt werden kann.
Stimmt und tut mir leid.
> "For the arithmetic operators, if any operand bit value is the unknown> value x or the high-impedance value z, then the entire result value> shall be x."
Das kommt mir etwas so vor, als würde man 0=1 per Definition festlegen.
Es ist halt einfach nicht so.
Morin schrieb:>> Der boolsche Wert von 1'bx ist "false". Aber wenn Du das x in einen>> definierten Wert wandelst, solltest Du dann nicht besser von>> vorneherein einen definierten Wert verwenden?>> Dann würde ich die Optimierung bei der Synthese unnötig> einschränken.
Ob Du dem Signal bereits bei der Erzeugung fest 0/1 zuweist, oder erst
in einem nachgeschalteten Schritt, sollte ziemlich egal sein.
>> "For the arithmetic operators, if any operand bit value is the unknown>> value x or the high-impedance value z, then the entire result value>> shall be x.">> Das kommt mir etwas so vor, als würde man 0=1 per Definition festlegen.> Es ist halt einfach nicht so.
Na ja, der Unterschied ist schon erheblich. Im Gegensatz zu 0=1 is das
Verhalten der Arithmetik ist ja nicht grundsätzlich falsch. Nur etwas
zu pingelig für den Geschmack einiger.
Was genau die ursprüngliche Begründung für dieses Verhalten ist, weiß
ich auch nicht. Ich kann nur vermuten, dass man die Synthese so wenig
wie möglich einschränken wollte :-)
Fakt ist jedenfalls, dass sich Verilog, VHDL/numeric_std und
VHDL/std_logic_arith in diesem Verhalten gleichen.
Gruß
Marcus
> Ob Du dem Signal bereits bei der Erzeugung fest 0/1 zuweist, oder erst> in einem nachgeschalteten Schritt, sollte ziemlich egal sein.
Ich weise ihm ja nichts festes zu. Das Konstrukt, das ich mir da
gebastelt habe, ist für die Simulation eine feste Zuweisung von 0 (wenn
das die if-Interpretation von x ist), für die Synthese ein don't-care.
> Na ja, der Unterschied ist schon erheblich. Im Gegensatz zu 0=1 is das> Verhalten der Arithmetik ist ja nicht grundsätzlich falsch. Nur etwas> zu pingelig für den Geschmack einiger.
Nun gut, rein formal ist "richtig" und "falsch" natürlich
Definitionssache.
Andererseits werden hier halt Signale mit einem klar definierten 0-Wert
(und andere mit einem klar definierten 1-Wert) auf x gesetzt, nur weil
ein völlig anderes Signal, welches in die Berechnung in keiner Weise
einfließt, auf x steht. Ein solches "übersprechen" von x-Werten auf
völlig andere Signale gibt es ja sonst auch in keiner Weise.
Der Wert x bedeutet doch "undefiniert". Ein Signal, das in jedem Fall 0
ist, ist nicht undefiniert.
Morin schrieb:>> Ob Du dem Signal bereits bei der Erzeugung fest 0/1 zuweist, oder erst>> in einem nachgeschalteten Schritt, sollte ziemlich egal sein.>> Ich weise ihm ja nichts festes zu.
Schon klar. Ich meinte damit "keinen undefinierten Wert".
Du hast keinen Einfluss darauf, wie die Synthese den Code
bearbeitet. Ich würde mich nicht darauf verlassen, dass die Synthese
generell den "Simulationscode" ignoriert. Aus deren Sicht ist das
Signal aluSignedComparison in jedem Fall 0 oder 1 wenn es ausgewertet
wird. Ob das Signal zwischendurch mal x war oder nicht, kann für die
Synthese Bedeutung haben haben, muss aber nicht.
Sieh das ganze mal etwas pragmatischer. Du wirst nicht drei
übereinstimmende Standards ändern können. Die Tatsache, dass sie
übereinstimmen sollte bereits Grund zum Feiern sein.
Die Nachteile unterschiedlichen Codes für Simulation/Synthese fallen
da allemal stärker ins Gewicht. Die Logik mit der das Signal generiert
wird ist ja nun auch nicht so komplex, dass man da -- Optimierung hin-
oder her -- mit dem default-Zweig viel reißen könnte.
> Andererseits werden hier halt Signale mit einem klar definierten 0-Wert> (und andere mit einem klar definierten 1-Wert) auf x gesetzt, nur weil> ein völlig anderes Signal, welches in die Berechnung in keiner Weise> einfließt, auf x steht. Ein solches "übersprechen" von x-Werten auf> völlig andere Signale gibt es ja sonst auch in keiner Weise.
Möglicherweise wollten die Urheber dieses Verhaltens bei
arithmetischen Operationen (im Gegensatz zu Bit-Operatoren) von einer
möglichen physikalischen Repräsentation abstrahieren.
Oder man wollte schlicht die Implementierung der Simulation
vereinfachen. Man testet erst, ob das Signal zweiwertig ist, und kann
das dann mit normalen Maschinenoperationen bearbeiten. Ansonsten setzt
man das gesamte Ergebnis auf x. Viel effizienter als das in
carry-ripple Manier nachzubilden.
Gruß
Marcus
> Du hast keinen Einfluss darauf, wie die Synthese den Code> bearbeitet. Ich würde mich nicht darauf verlassen, dass die Synthese> generell den "Simulationscode" ignoriert. Aus deren Sicht ist das> Signal aluSignedComparison in jedem Fall 0 oder 1 wenn es ausgewertet> wird. Ob das Signal zwischendurch mal x war oder nicht, kann für die> Synthese Bedeutung haben haben, muss aber nicht.
Da hast du natürlich recht. Letztendlich kommt es mir auf solche
Kleinigkeiten auch nicht wirklich an, da könnte ich viel größere
Optimierungen vornehmen. Wenn es nötig wäre, würde ich hier auch einen
definierten Wert statt x nehmen.
> Die Nachteile unterschiedlichen Codes für Simulation/Synthese fallen> da allemal stärker ins Gewicht. Die Logik mit der das Signal generiert> wird ist ja nun auch nicht so komplex, dass man da -- Optimierung hin-> oder her -- mit dem default-Zweig viel reißen könnte.
Das Ursprüngliche Problem ohne meinen Fix war ja gerade, dass es
unterschiedliches Verhalten zwischen Simulation und Synthese gab.
Konkret sind meine Unit-Tests mit Fehlern abgebrochen, weil bei der
Addition "x" rauskam, obwohl die Schaltung korrekt war.