Forum: FPGA, VHDL & Co. Verilog: Probleme mit Don't Care


von Morin (Gast)


Lesenswert?

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:
1
...
2
reg[31:0] aluLeftOperand;
3
reg[31:0] aluRightOperand;
4
reg aluSignedComparison;
5
...
6
wire[32:0] aluExtendedLeftOperand = {aluSignedComparison & aluLeftOperand[31], aluLeftOperand};
7
wire[32:0] aluExtendedRightOperand = {aluSignedComparison & aluRightOperand[31], aluRightOperand};
8
...
9
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.
38
 */
39
wire aluOperandsEqual = (aluLeftOperand == aluRightOperand);
40
 
41
/****************************************************************************/
42
43
/**
44
 * To use the ALU for comparison, set this bit to 0 for unsigned
45
 * mode, 1 for signed mode. This register has no effect on
46
 * normal computation results.
47
 */
48
reg aluSignedComparison;
49
50
/**
51
 * Set this condition code for comparison operations.
52
 * The aluOperationRegister MUST be set to subtraction
53
 * for comparison operations.
54
 */
55
reg[2:0] aluComparisonOperation;
56
57
/**
58
 * This result is computed asynchronously from the operands,
59
 * ALU operation (must be subtraction), and comparison operation.
60
 * Note that this is a "reg" in Verilog terms, but not an actual register.
61
 */
62
reg aluComparisonResult;
63
always @(*) begin
64
  case (aluComparisonOperation)
65
    `ALU_COMPARISON_EQUAL:
66
      aluComparisonResult <= aluOperandsEqual;
67
    `ALU_COMPARISON_NOT_EQUAL:
68
      aluComparisonResult <= ~aluOperandsEqual;
69
    `ALU_COMPARISON_LESS_THAN:
70
      aluComparisonResult <= aluExtendedResult[32];
71
    `ALU_COMPARISON_LESS_EQUAL:
72
      aluComparisonResult <= aluOperandsEqual | aluExtendedResult[32];
73
    `ALU_COMPARISON_GREATER_THAN:
74
      aluComparisonResult <= ~aluOperandsEqual & ~aluExtendedResult[32];
75
    `ALU_COMPARISON_GREATER_EQUAL:
76
      aluComparisonResult <= ~aluExtendedResult[32];
77
    default:
78
      aluComparisonResult <= 1'bx;
79
  endcase
80
end
81
82
/****************************************************************************/
83
84
/**
85
 * The 33-bit sign-extended or zero-extended left operand.
86
 */
87
wire[32:0] aluExtendedLeftOperand = {aluSignedComparison & aluLeftOperand[31], aluLeftOperand};
88
89
/**
90
 * The 33-bit sign-extended or zero-extended right operand.
91
 */
92
wire[32:0] aluExtendedRightOperand = {aluSignedComparison & aluRightOperand[31], aluRightOperand};
93
94
/**
95
 * This result is computed asynchronously from the operands
96
 * and operation.
97
 */
98
reg[32:0] aluExtendedResult;
99
always @(*) begin
100
  case (aluOperationRegister)
101
    `ALU_OPERATION_ADD:
102
      aluExtendedResult <= aluExtendedLeftOperand + aluExtendedRightOperand;
103
    `ALU_OPERATION_SUB:
104
      aluExtendedResult <= aluExtendedLeftOperand - aluExtendedRightOperand;
105
    `ALU_OPERATION_HIGH:
106
      aluExtendedResult <= {1'bx, aluExtendedRightOperand[15:0], 16'h0000};
107
    `ALU_OPERATION_XOR:
108
      aluExtendedResult <= aluExtendedLeftOperand ^ aluExtendedRightOperand;
109
    `ALU_OPERATION_XNOR:
110
      aluExtendedResult <= aluExtendedLeftOperand ~^ aluExtendedRightOperand;
111
    `ALU_OPERATION_AND:
112
      aluExtendedResult <= aluExtendedLeftOperand & aluExtendedRightOperand;
113
    `ALU_OPERATION_OR:
114
      aluExtendedResult <= aluExtendedLeftOperand | aluExtendedRightOperand;
115
    default:
116
      aluExtendedResult <=33'bxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
117
  endcase
118
end

von Marcus H. (mharnisch) Benutzerseite


Lesenswert?

Bist auch ganz sicher, dass "case (aluOperationRegister)" nicht im 
default Zweig landet?

Es würde natürlich helfen, den ganzen Code zu sehen...

--
Marcus

von Morin (Gast)


Lesenswert?

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?

von Marcus H. (mharnisch) Benutzerseite


Lesenswert?

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

von Marcus H. (mharnisch) Benutzerseite


Lesenswert?

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

von Morin (Gast)


Lesenswert?

> 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.

von Marcus H. (mharnisch) Benutzerseite


Lesenswert?

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

von Morin (Gast)


Lesenswert?

> 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.

von Marcus H. (mharnisch) Benutzerseite


Lesenswert?

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

von Morin (Gast)


Lesenswert?

> 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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.