FPGARelated.com
Forums

What is wrong with low level code?

Started by Kevin Simonson October 4, 2020
I recently posted a Verilog module to this forum, and someone responded that I was coding at a very low level, which was true; I was referring to XOR gates, NAND gates, NOR gates, and NOT gates. Is there something wrong with writing my code at such a low level? If I have a fairly good understanding of how my algorithm needs to run at such a low level, then what is wrong with writing that kind of low level code?
On Sunday, October 4, 2020 at 8:02:07 PM UTC-4, Kevin Simonson wrote:
> I recently posted a Verilog module to this forum, and someone responded that I was coding at a very low level, which was true; I was referring to XOR gates, NAND gates, NOR gates, and NOT gates. Is there something wrong with writing my code at such a low level? If I have a fairly good understanding of how my algorithm needs to run at such a low level, then what is wrong with writing that kind of low level code?
Nothing wrong with it as such, but it is less efficient and requires a lot of extra typing. When you say you "code at a very low level" I assume you are instantiating gates and wiring them together rather than just inferring gates using logic operators in assignments? If so, you can infer many, many gates with a simple assignment of an expression to a signal like, A <= B AND C; where A, B and C are each vectors or individual signals. I'm not a Verilog user, so I don't know the exact syntax. I want to say the logic operators are symbols rather than words, but it's the same concept. Is this method awkward to you? -- Rick C. - Get 1,000 miles of free Supercharging - Tesla referral code - https://ts.la/richard11209
Kevin Simonson <kvnsmnsn@hotmail.com> wrote:
> I recently posted a Verilog module to this forum, and someone responded > that I was coding at a very low level, which was true; I was referring to > XOR gates, NAND gates, NOR gates, and NOT gates. Is there something wrong > with writing my code at such a low level? If I have a fairly good > understanding of how my algorithm needs to run at such a low level, then > what is wrong with writing that kind of low level code?
It's not wrong, but: 1. It may make your code harder for read, for someone who isn't familiar with its workings. That someone could be yourself if you come back to the code in a decade's time. 2. The task you're trying to do probably doesn't fall naturally into a representation of NAND and NOR gates. So why not enter the problem in its natural state, rather than transform it first in your head? That way there's fewer opportunities for mistakes. 3. It may lull you into a false sense of security, because (unless you're instantiating vendor-specific primitives) the first thing any synthesis tool will do is throw away your gate representation. At which point the assumptions you may bring to your gate representation (eg about gate delays) no longer hold. Theo
On Sunday, October 4, 2020 at 8:02:07 PM UTC-4, Kevin Simonson wrote:
> I recently posted a Verilog module to this forum, and someone responded that I was coding at a very low level, which was true; I was referring to XOR gates, NAND gates, NOR gates, and NOT gates. Is there something wrong with writing my code at such a low level? If I have a fairly good understanding of how my algorithm needs to run at such a low level, then what is wrong with writing that kind of low level code?
Unless the algorithm specification that you're trying to implement is written in terms of such gates, then long term support of that code would be a reason why coding at a low level is 'wrong'. Kevin Jennings
On 05/10/2020 01:02:03, Kevin Simonson wrote:
> I recently posted a Verilog module to this forum, and someone > responded that I was coding at a very low level, which was true; I > was referring to XOR gates, NAND gates, NOR gates, and NOT gates. Is > there something wrong with writing my code at such a low level? If I > have a fairly good understanding of how my algorithm needs to run at > such a low level, then what is wrong with writing that kind of low > level code?
As others have said readability is everything. One time when I wrote at a low level was when trying to maximise speed when pipe-lining numerical algorithms. There are times when it's appropriate. -- Mike Perkins Video Solutions Ltd www.videosolutions.ltd.uk
On Tuesday, October 6, 2020 at 7:04:31 PM UTC-4, Mike Perkins wrote:
> On 05/10/2020 01:02:03, Kevin Simonson wrote: > > I recently posted a Verilog module to this forum, and someone > > responded that I was coding at a very low level, which was true; I > > was referring to XOR gates, NAND gates, NOR gates, and NOT gates. Is > > there something wrong with writing my code at such a low level? If I > > have a fairly good understanding of how my algorithm needs to run at > > such a low level, then what is wrong with writing that kind of low > > level code? >=20 > As others have said readability is everything. >=20 > One time when I wrote at a low level was when trying to maximise speed=20 > when pipe-lining numerical algorithms. >=20 > There are times when it's appropriate.
If not in this thread I may have mentioned a former poster here who used sc= hematics for hierarchical construction allowing specification of relative a= nd absolute positioning within a layout. He only converted to VHDL when he= was able to do the same specification of location attributes. Then he nev= er looked back. He was essentially designing at the LUT level and simplify= ing the design process by using hierarchical constructs eliminating much of= the detail design process. =20 This was largely practical for his work because he was designing similar ci= rcuits repeatedly, but mostly because he was being paid to do designs no on= e else could pull off really. The combination of speed and density was har= d to achieve. Today the tools are better and the parts are MUCH bigger mak= ing large, fast designs easier to do without massive libraries of hand hewn= designs much less common.=20 So wiring together such things as gates is very uncommon these days. It is= well worth the while to learn how to describe your design in HDL rather th= an instantiating gates and such.=20 --=20 Rick C. + Get 1,000 miles of free Supercharging + Tesla referral code - https://ts.la/richard11209
On Tuesday, October 6, 2020 at 5:22:11 PM UTC-7, gnuarm.del...@gmail.com wr=
ote:
> So wiring together such things as gates is very uncommon these days. It i=
s well worth the while to learn how to describe your design in HDL rather t= han instantiating gates and such.=20
>=20 > --=20 >=20 > Rick C.=20
Okay, I'm willing to learn how to describe my design in HDL, preferably Ver= ilog, since that's what I've concentrated on. How would I go about learning= how to write higher level Verilog, rather "than instantiating gates and su= ch"? I'll include my source code as it stands right now in my next post. I = guess I'd just like to know how to code it at a higher level.
// (c) Kevin Simonson 2020

module lessThan
  #( parameter nmBits = 1)
  ( output              lssThn
  , input [ nmBits-1:0] leftOp
  , input [ nmBits-1:0] rightOp);

typedef enum { CORNER, E_LEAF, N_LEAF, SIDE, E_INTERIOR, N_INTERIOR } nodeType;

localparam integer nmNodes  = (nmBits << 1) - 1;
localparam integer lvLimit  = $clog2( nmBits);
localparam integer nmRships = (nmNodes << 1) - lvLimit - 1;

nodeType ndTypes [  nmNodes:1];
integer  inLows  [  nmNodes:1];
integer  inHighs [ nmBits-1:1];
integer  outs    [  nmNodes:1];
integer  bases   [  lvLimit:0];
wire     rss     [ nmRships:1];
wire     ntRght;
genvar   gnIx;

function automatic integer fillSubtree ( input integer vrtcl
                                       , input integer hrzntl
                                       , input     bit equal);
    integer ndIx;
    integer rlIx;
    integer vr;
    integer hz;
    integer twice;
    integer nxVr;
    bit nxEq;
  begin
    ndIx = (1 << vrtcl) + (hrzntl << vrtcl + 1);
    vr   = vrtcl;
    hz   = hrzntl;
    while (nmNodes < ndIx)
    begin
      vr   = vr - 1;
      hz <<= 1;
      ndIx = (1 << vr) + (hz << vr + 1);
    end
    rlIx        = bases[ vr] + (hz << 1);
    fillSubtree = rlIx;
    if (0 < vr)
    begin
      nxVr                = vr - 1;
      twice               = hz << 1;
      nxEq                = 0 == hz || ! equal;
      ndTypes[ ndIx]      = 0 < hz ? equal ? E_INTERIOR : N_INTERIOR : SIDE;
      inLows[ ndIx]       = fillSubtree( nxVr, twice    , nxEq);
      inHighs[ ndIx >> 1] = fillSubtree( nxVr, twice + 1, nxEq);
    end
    else
    begin
      ndTypes[ ndIx] = 0 < hz ? equal ? E_LEAF : N_LEAF : CORNER;
      inLows[ ndIx]  = hz;
    end
    outs[ ndIx] = rlIx;
  end
endfunction

initial
begin
  integer lvl;
  bases[ 0] = 1;
  for (lvl = 0; lvl < lvLimit; lvl = lvl + 1)
  begin
    bases[ lvl + 1] = bases[ lvl] + ((nmNodes >> lvl) + 1 >> 1 << 1) - 1;
  end
  fillSubtree( lvLimit, 0, 1);
end

generate
  for (gnIx = 1; gnIx <= nmNodes; gnIx = gnIx + 1)
  begin
    integer inLow  = inLows[ gnIx];
    integer inHigh = inHighs[ gnIx + 1 >> 1];
    integer out    = outs[ gnIx];
    case (ndTypes[ gnIx])
      E_INTERIOR
    : begin
        nor2 pio( rss[ out - 1], rss[ inLow - 1], rss[ inHigh - 1]);
        mplex pim( rss[ out], rss[ inHigh - 1], rss[ inHigh], rss[ inLow]);
      end
      N_INTERIOR
    : begin
        nand2 mia( rss[ out - 1], rss[ inLow - 1], rss[ inHigh - 1]);
        mplex mim( rss[ out], rss[ inHigh - 1], rss[ inLow], rss[ inHigh]);
      end
      SIDE
    : mplex sm( rss[ out], rss[ inHigh - 1], rss[ inLow], rss[ inHigh]);
      E_LEAF
    : begin
        equ2 ple( rss[ out], leftOp[ inLow], rightOp[ inLow]);
        assign rss[ out - 1] = rightOp[ inLow];
      end
      N_LEAF
    : begin
        xor2 mlx( rss[ out], leftOp[ inLow], rightOp[ inLow]);
        assign rss[ out - 1] = rightOp[ inLow];
      end
      CORNER
    : begin
        nt1 cn( ntRght, rightOp[ inLow]);
        nor2 co( rss[ out], leftOp[ inLow], ntRght);
      end
    endcase
  end
endgenerate
assign lssThn = rss[ bases[ lvLimit]];

endmodule
On Monday, October 12, 2020 at 7:27:49 PM UTC-4, Kevin Simonson wrote:
> On Tuesday, October 6, 2020 at 5:22:11 PM UTC-7, gnuarm.del...@gmail.com =
wrote:
> > So wiring together such things as gates is very uncommon these days. It=
is well worth the while to learn how to describe your design in HDL rather= than instantiating gates and such.=20
> >=20 > > --=20 > >=20 > > Rick C.=20 >=20 > Okay, I'm willing to learn how to describe my design in HDL, preferably V=
erilog, since that's what I've concentrated on. How would I go about learni= ng how to write higher level Verilog, rather "than instantiating gates and = such"? I'll include my source code as it stands right now in my next post. = I guess I'd just like to know how to code it at a higher level. Sorry, it is too painful for me to read that sort of code. I don't say tha= t as an insult, simply that Verilog is a second language to me so I'm not c= lear what all the operators are, etc. =20 If you want to add two numbers, you write=20 A <=3D B + C;=20 That's it in VHDL anyway. I think in this case Verilog is the same. =20 But your code below is not instantiating gates that I can see.=20 Let's try this. How about showing what your code does with a simple math s= tatement if that is possible? Not HDL, just an explanation, a spec like so= meone might give you to indicate what the calculation would do without spec= ifying any of the implementation details. =20 --=20 Rick C. -- Get 1,000 miles of free Supercharging -- Tesla referral code - https://ts.la/richard11209
On Monday, October 12, 2020 at 7:30:21 PM UTC-4, Kevin Simonson wrote:
> // (c) Kevin Simonson 2020 > > module lessThan > #( parameter nmBits = 1) > ( output lssThn > , input [ nmBits-1:0] leftOp > , input [ nmBits-1:0] rightOp); > > typedef enum { CORNER, E_LEAF, N_LEAF, SIDE, E_INTERIOR, N_INTERIOR } nodeType; > > localparam integer nmNodes = (nmBits << 1) - 1; > localparam integer lvLimit = $clog2( nmBits); > localparam integer nmRships = (nmNodes << 1) - lvLimit - 1; > > nodeType ndTypes [ nmNodes:1]; > integer inLows [ nmNodes:1]; > integer inHighs [ nmBits-1:1]; > integer outs [ nmNodes:1]; > integer bases [ lvLimit:0]; > wire rss [ nmRships:1]; > wire ntRght; > genvar gnIx; > > function automatic integer fillSubtree ( input integer vrtcl > , input integer hrzntl > , input bit equal); > integer ndIx; > integer rlIx; > integer vr; > integer hz; > integer twice; > integer nxVr; > bit nxEq; > begin > ndIx = (1 << vrtcl) + (hrzntl << vrtcl + 1); > vr = vrtcl; > hz = hrzntl; > while (nmNodes < ndIx) > begin > vr = vr - 1; > hz <<= 1; > ndIx = (1 << vr) + (hz << vr + 1); > end > rlIx = bases[ vr] + (hz << 1); > fillSubtree = rlIx; > if (0 < vr) > begin > nxVr = vr - 1; > twice = hz << 1; > nxEq = 0 == hz || ! equal; > ndTypes[ ndIx] = 0 < hz ? equal ? E_INTERIOR : N_INTERIOR : SIDE; > inLows[ ndIx] = fillSubtree( nxVr, twice , nxEq); > inHighs[ ndIx >> 1] = fillSubtree( nxVr, twice + 1, nxEq); > end > else > begin > ndTypes[ ndIx] = 0 < hz ? equal ? E_LEAF : N_LEAF : CORNER; > inLows[ ndIx] = hz; > end > outs[ ndIx] = rlIx; > end > endfunction > > initial > begin > integer lvl; > bases[ 0] = 1; > for (lvl = 0; lvl < lvLimit; lvl = lvl + 1) > begin > bases[ lvl + 1] = bases[ lvl] + ((nmNodes >> lvl) + 1 >> 1 << 1) - 1; > end > fillSubtree( lvLimit, 0, 1); > end > > generate > for (gnIx = 1; gnIx <= nmNodes; gnIx = gnIx + 1) > begin > integer inLow = inLows[ gnIx]; > integer inHigh = inHighs[ gnIx + 1 >> 1]; > integer out = outs[ gnIx]; > case (ndTypes[ gnIx]) > E_INTERIOR > : begin > nor2 pio( rss[ out - 1], rss[ inLow - 1], rss[ inHigh - 1]); > mplex pim( rss[ out], rss[ inHigh - 1], rss[ inHigh], rss[ inLow]); > end > N_INTERIOR > : begin > nand2 mia( rss[ out - 1], rss[ inLow - 1], rss[ inHigh - 1]); > mplex mim( rss[ out], rss[ inHigh - 1], rss[ inLow], rss[ inHigh]); > end > SIDE > : mplex sm( rss[ out], rss[ inHigh - 1], rss[ inLow], rss[ inHigh]); > E_LEAF > : begin > equ2 ple( rss[ out], leftOp[ inLow], rightOp[ inLow]); > assign rss[ out - 1] = rightOp[ inLow]; > end > N_LEAF > : begin > xor2 mlx( rss[ out], leftOp[ inLow], rightOp[ inLow]); > assign rss[ out - 1] = rightOp[ inLow]; > end > CORNER > : begin > nt1 cn( ntRght, rightOp[ inLow]); > nor2 co( rss[ out], leftOp[ inLow], ntRght); > end > endcase > end > endgenerate > assign lssThn = rss[ bases[ lvLimit]]; > > endmodule
I found the top level module declaration (third line, duh!) and it looks like you are implementing a subtraction, I'm guessing the carry out is the flag. In RTL this would be something like... Out <= '1' when leftOp < rightOp else '0'; No need to even define a function/module. At least in VHDL this is built into the language and I'm pretty sure something equivalent is provided as well. From what I recall, you were specifying all the details to provide a speed optimized solution. That is probably counter productive in an FPGA and may or may not be in something like a gate array. I don't know so much about fully synthesized ASICs. Is that more clear? You might try implementing something like this and compare the result to your highly specified code in synthesis. So how they both turn out. -- Rick C. -+ Get 1,000 miles of free Supercharging -+ Tesla referral code - https://ts.la/richard11209