Décimal densément emballé (DPD) à décimal, avec portes logiques

13

Inspiré par la popularité récente de nandgame sur TNB, et mon propre défi précédent .

Contexte

La décimale dense (DPD) est un moyen de stocker efficacement les chiffres décimaux en binaire. Il stocke trois chiffres décimaux (000 à 999) sur 10 bits, ce qui est beaucoup plus efficace que BCD naïf (qui stocke un chiffre sur 4 bits).

Table de conversion

DPD est conçu pour convertir facilement entre les bits et les chiffres par simple correspondance de motifs de haut en bas. Chaque motif binaire définit le nombre de chiffres élevés (8-9) du nombre, où ils se trouvent et comment déplacer les bits pour former la représentation décimale.

Ce qui suit est la table de conversion de 10 bits de DPD à trois chiffres décimaux. Chaque chiffre décimal est représenté en binaire 4 bits (BCD). Les deux côtés sont écrits de gauche à droite du chiffre le plus significatif au moins.

Bits                 =>  Decimal         (Digit range)

a b c d e f 0 g h i  =>  0abc 0def 0ghi  (0-7) (0-7) (0-7)

a b c d e f 1 0 0 i  =>  0abc 0def 100i  (0–7) (0–7) (8–9)
a b c g h f 1 0 1 i  =>  0abc 100f 0ghi  (0–7) (8–9) (0–7)
g h c d e f 1 1 0 i  =>  100c 0def 0ghi  (8–9) (0–7) (0–7)

g h c 0 0 f 1 1 1 i  =>  100c 100f 0ghi  (8–9) (8–9) (0–7)
d e c 0 1 f 1 1 1 i  =>  100c 0def 100i  (8–9) (0–7) (8–9)
a b c 1 0 f 1 1 1 i  =>  0abc 100f 100i  (0–7) (8–9) (8–9)
x x c 1 1 f 1 1 1 i  =>  100c 100f 100i  (8–9) (8–9) (8–9)

Notations

  • Les lettres minuscules aà isont les bits copiés dans la représentation décimale.
  • 0et 1sont les bits exacts dans les modèles de bits d'entrée ou de sortie.
  • x les bits sont ignorés dans la conversion.

Tâche

Créez un circuit logique à l'aide de portes NAND à deux entrées pour convertir 10 bits de DPD en 12 bits de BCD.

Exemples

Les bits accentués sont les bits de correspondance de modèle.

DPD                    Decimal  BCD
0 0 0 0 0 0 0 1 0 1    005      0000 0000 0101
            ^
0 0 0 1 1 0 0 0 1 1    063      0000 0110 0011
            ^
0 0 0 1 1 1 1 0 0 1    079      0000 0111 1001
            ^ ^ ^
0 0 0 0 0 1 1 0 1 0    090      0000 1001 0000
            ^ ^ ^
0 0 0 1 0 1 1 1 1 0    098      0000 1001 1000
      ^ ^   ^ ^ ^
1 0 1 0 1 1 1 0 1 0    592      0101 1001 0010
            ^ ^ ^
0 0 1 1 0 0 1 1 0 1    941      1001 0100 0001
            ^ ^ ^
1 1 0 0 1 1 1 1 1 1    879      1000 0111 1001
      ^ ^   ^ ^ ^
1 1 1 0 0 0 1 1 1 0    986      1001 1000 0110
      ^ ^   ^ ^ ^
0 0 1 1 1 1 1 1 1 1    999      1001 1001 1001
      ^ ^   ^ ^ ^
1 1 1 1 1 1 1 1 1 1    999      1001 1001 1001
      ^ ^   ^ ^ ^

Critère de notation et de victoire

Le score est le nombre de portes NAND à deux entrées utilisées dans votre circuit. Le score le plus bas l'emporte.

Vous pouvez définir de petits composants en termes de portes NAND à deux entrées, puis les utiliser dans votre construction finale. Si un composant Xcomprend Ndes portes NAND à deux entrées, chaque utilisation de Xajoute Nà votre score. Pour les portes logiques de base, cela signifie:

  • PAS: +1
  • 2 entrées ET: +2
  • 2 entrées OU: +3
  • XOR 2 entrées: +4
Bubbler
la source
Annulé la modification de Luis, car atomic-code-golf est une balise de critère gagnant et code-challenge est pour les questions qui ont un critère de gain non couvert par d'autres balises.
Peter Taylor
Ce n'est toujours pas clair pour moi. Je pense qu'il doit y avoir une description plus détaillée de ce que les lettres aà idire et le processus de conversion. Passez par les étapes, plutôt que de simplement montrer des exemples et en espérant que nous comprenons de cela.
mbomb007
@ mbomb007, c'est peut-être juste clair pour moi parce que l'une de mes langues est SML. Ce premier bloc de code est pratiquement une implémentation de référence dans un langage de correspondance de motifs (bien qu'il fonctionne mieux en SMLNJ, qui fait écho au résultat de chaque instruction, qu'en MLton).
Peter Taylor
@ mbomb007 J'ai essayé de clarifier la nature de correspondance de motifs de la table de conversion. Aide-t-il?
Bubbler
1
@Bubbler Ouais, c'est utile
mbomb007

Réponses:

4

45 NAND (ou 43)

45 semble être le minimum absolu, mais il est possible d'atteindre 43 NAND par une astuce: en supposant que les plus grands nombres sont correctement encodés.

888, 889, 898, 899, 988, 989, 998, 999 doivent être codés avec le 2 MSB en 00, nécessitant seulement 43 NAND pour le décodage.

Cependant, dans la spécification de décodage, ils sont spécifiés pour être ignorés, ce qui signifie qu'ils peuvent être n'importe quoi. Il est raisonnable de supposer que cette spécification plus libre pourrait nécessiter encore moins de portes, mais l'inverse est vrai. 45 portes sont nécessaires pour cela. Cette économie pourrait apporter de réels avantages à de vrais circuits.

J'ai également trouvé des circuits beaucoup plus efficaces et plus rapides, contenant quelques portes de plus.

Pas d'image au crayon dessinée du circuit cette fois. Peut-être plus tard.

Le circuit est présenté en code Verilog évident, prêt à fonctionner avec test inclus.

Code Verilog:

// Densely packed decimal (DPD) to decimal, circuit in Verilog.
// 45 NANDs only, which seems to be minimal.
//
// By Kim Øyhus 2019 (c) into (CC BY-SA 3.0.)
// This work is licensed under the Creative Commons Attribution 3.0
// Unported License. To view a copy of this license, visit
// https://creativecommons.org/licenses/by-sa/3.0/
//
// This is my entry to win this Programming Puzzle & Code Golf
// at Stack Exchange: 
// /codegolf/176557/densely-packed-decimal-dpd-to-decimal-with-logic-gates
//
// TASK:
// 3 decimal digits are stored in 10 bits in the DPD format,
// and this circuit transforms them into 3 decimal digits in
// 4 bits each, BCD format.
//
// 45 gates seem to be the smallest possible NAND circuit there is
// for this task, but I can get even lower by a trick, to 43:
// I assume that the largest numbers are correctly encoded.
//   888, 889, 898, 899, 988, 989, 998, 999 are to be encoded
// with the 2 MSB as 00, requiring just 43 NANDs for decoding.
//
//   However, in the specification for decoding, they are specified
// to be ignored, meaning they can be anything. It is a reasonable
// assumption that this freer specification could require even fewer
// gates, but the opposite is true. 45 gates are required for this.
// This saving could give real benefits for real circuits.
//
//   This DPD format seems to be used a lot for storing decimal numbers
// in computers and IO for ALUs, even though it is stored as 12 bits
// per 3 digits inside the ALUs and for other calculations.
// It is also used in many patents.


module decode1000 ( in_000, in_001, in_002, in_003, in_004, in_005, in_006, in_007, in_008, in_009, out000, out001, out002, out003, out004, out005, out006, out007, out008, out009, out010, out011 );
  input  in_000, in_001, in_002, in_003, in_004, in_005, in_006, in_007, in_008, in_009;
  output out000, out001, out002, out003, out004, out005, out006, out007, out008, out009, out010, out011;
  wire   wir000, wir001, wir002, wir003, wir004, wir005, wir006, wir007, wir008, wir009, wir010, wir011, wir012, wir013, wir014, wir015, wir016, wir017, wir018, wir019, wir020, wir021, wir022, wir023, wir024, wir025, wir026, wir027, wir028, wir029, wir030, wir031, wir032;

  nand gate000 ( wir000, in_007, in_007 );
  nand gate001 ( wir001, in_003, in_001 );
  nand gate002 ( wir002, in_002, in_003 );
  nand gate003 ( wir003, wir001, in_006 );
  nand gate004 ( wir004, wir002, in_001 );
  nand gate005 ( wir005, wir001, wir001 );
  nand gate006 ( wir006, in_005, in_001 );
  nand gate007 ( wir007, wir006, in_003 );
  nand gate008 ( out008, wir000, wir000 );
  nand gate009 ( wir008, wir004, wir007 );
  nand gate010 ( wir009, wir005, in_006 );
  nand gate011 ( wir010, wir007, wir007 );
  nand gate012 ( wir011, wir009, in_002 );
  nand gate013 ( wir012, wir011, wir009 );
  nand gate014 ( wir013, wir011, wir011 );
  nand gate015 ( wir014, in_008, wir013 );
  nand gate016 ( wir015, in_009, wir013 );
  nand gate017 ( wir016, wir010, wir014 );
  nand gate018 ( wir017, wir014, wir005 );
  nand gate019 ( wir018, wir015, wir015 );
  nand gate020 ( wir019, wir011, wir008 );
  nand gate021 ( wir020, wir019, wir006 );
  nand gate022 ( wir021, wir010, wir018 );
  nand gate023 ( wir022, wir020, wir004 );
  nand gate024 ( wir023, wir016, wir008 );
  nand gate025 ( out001, wir023, wir023 );
  nand gate026 ( out003, wir022, wir022 );
  nand gate027 ( wir024, wir005, wir008 );
  nand gate028 ( wir025, wir012, wir002 );
  nand gate029 ( wir026, wir019, in_003 );
  nand gate030 ( wir027, in_004, in_004 );
  nand gate031 ( out007, wir024, wir009 );
  nand gate032 ( out011, wir026, wir026 );
  nand gate033 ( wir028, wir017, in_005 );
  nand gate034 ( wir029, in_000, in_000 );
  nand gate035 ( wir030, wir026, in_008 );
  nand gate036 ( out005, wir028, wir028 );
  nand gate037 ( out009, wir030, wir030 );
  nand gate038 ( out000, wir029, wir029 );
  nand gate039 ( wir031, wir026, in_009 );
  nand gate040 ( out004, wir027, wir027 );
  nand gate041 ( out010, wir031, wir031 );
  nand gate042 ( wir032, out003, wir018 );
  nand gate043 ( out006, wir003, wir032 );
  nand gate044 ( out002, wir025, wir021 );
endmodule

module test;
   reg  [ 9:0] AB; // input DPD
   wire [11:0] C; // output BCD

  decode1000 U1 ( 
  .in_000 (AB[ 0]), 
  .in_001 (AB[ 1]), 
  .in_002 (AB[ 2]), 
  .in_003 (AB[ 3]), 
  .in_004 (AB[ 4]), 
  .in_005 (AB[ 5]), 
  .in_006 (AB[ 6]), 
  .in_007 (AB[ 7]), 
  .in_008 (AB[ 8]), 
  .in_009 (AB[ 9]), 
  .out000 ( C[ 0]),
  .out001 ( C[ 1]),
  .out002 ( C[ 2]),
  .out003 ( C[ 3]),
  .out004 ( C[ 4]),
  .out005 ( C[ 5]),
  .out006 ( C[ 6]),
  .out007 ( C[ 7]),
  .out008 ( C[ 8]),
  .out009 ( C[ 9]),
  .out010 ( C[10]),
  .out011 ( C[11])
  ); 

   initial  AB=0;  //unary=0;  binary=0
  always  #1  AB = AB+1;
  initial  begin
    $display("\t\ttime,\tinn 10bit   \tout 3x4bit"); 
    $monitor("%d,\t%b %b %b\t%b %b %b\t %d%d%d",$time, AB[9:7],AB[6:4],AB[3:0], C[11:8], C[7:4], C[3:0],  C[11:8], C[7:4], C[3:0]); 
  end 
  initial  #1023  $finish; 
endmodule

// How I run and test it:
// iverilog -o decode1000 decode1000.v
// vvp decode1000
KimOyhus
la source
3

65 62 60 58 NAND

En prenant les entrées que i0pour i9et les sorties que o0pour o9, oa, obnous

t0 = nand(i6, i7)
t1 = nand(t0, t0)
t2 = nand(i3, i4)
o0 = nand(nand(nand(t2, i3), t1), nand(nand(t2, i8), t1))
t3 = nand(o0, o0)
t4 = nand(i0, t3)
o1 = nand(t4, t4)
t5 = nand(i1, t3)
o2 = nand(t5, t5)
o3 = i2
# Score 13 for the first decimal digit

u0 = nand(i6, i8)
u1 = nand(u0, t0)
u2 = nand(i4, t2)
o4 = nand(nand(nand(u2, u0), u2), nand(u1, t0))
u3 = nand(o4, o4)
u4 = nand(o0, i8)
u5 = nand(u4, u4)
u6 = nand(u3, nand(nand(u5, i0), nand(u4, i3)))
o5 = nand(u6, u6)
u7 = nand(u3, nand(nand(u5, i1), nand(u4, i4)))
o6 = nand(u7, u7)
o7 = i5
# Score 20 for the second decimal digit

o8 = nand(nand(nand(nand(i4, i8), o0), t1), nand(nand(i6, u1), i6))
v2 = nand(o8, o8)
v3 = nand(i6, i6)
v4 = nand(i7, i7)
v5 = nand(v2, nand(nand(i6, nand(nand(i7, i0), nand(v4, i3))), nand(v3, i7)))
o9 = nand(v5, v5)
v6 = nand(v2, nand(nand(i6, nand(nand(i7, i1), nand(v4, i4))), nand(v3, i8)))
oa = nand(v6, v6)
ob = i9
# Score 25 for the third decimal digit

Framework de test Python pour valider l'exactitude de la construction.

Peter Taylor
la source