What features are missing from popular EDA languages?

Status
Not open for further replies.

vGoodtimes

Advanced Member level 4
Joined
Feb 16, 2015
Messages
1,089
Helped
307
Reputation
614
Reaction score
303
Trophy points
83
Visit site
Activity points
8,730
I've done a fair amount of RTL design. At first, there is a "how does this work" outlook. But eventually I started wondering "why doesn't this"...

So for fun, what are your most desired language features that just never got added?

For me:

1.) export combinatorial signals from clocked processes. Having access to next-state logic is amazingly useful if you don't have to do any work to get it. But multi-process designs are very verbose and can generate accidental latches.
2.) currying. Functions can't return types, subtypes, functions, or procedures/tasks. There are no lambda function or boost::bind equivalents. I started using procedures on fairly simple code to add meaning and to shrink the core of my FSM's. Much of it was just copy-paste and could have been handled by the language. (I guess this could also be templated functions/procedures).
3.) output generics/parameters. I've always liked the idea of auto-allocating the control address space of my design. I'd like to be able to have submodules advertise how many addresses they need and pass the info up to controllers that can then pass base addresses back down. There could be loops, but VHDL/Verilog can already deal with combinatorial loops, this is no different.
4.) assert exists. In all honesty, it is impossible to have no warnings in VHDL. Some designs rely on const-propagation to remove unused logic. But it would be nice to be able to assert that signal x (eg, "clk") is never removed. The tools could give more detailed info when the signal is removed.

In the end, it won't matter -- anything you suggest will at best be added to VHDL2100, or SystemVerilog2100...
 

In response to your gripes, I would primarily say why not put use cases in the VHDL/Verilog working groups? why not join the working group, rather than moan on a forum where your thoughts will get lost?

In response to your gripes:
1. Never been a problem for me.
2. I can sort of see your point, have you got a code example where returning a type would be useful? I have had a couple of situations where I would have liked a 'type attribute, but it was easy to work around.
3. Again - example?
4. I think this is the wrong level. HDL has no knowledge of how the synthesis is going to work, or what it's going to do. So clk will always exist at the RTL level.

My only problems with VHDL 2008 so far:

1. Cannot have an array of protected types (this is a gripe going back to 2002)
2. Cannot pass access types into and out of protected types (I can see why, but has made me do some annoying copying etc )
3. Generic packages cannot contain variable - variables can only be included in packages if the package is declared in a variable context (like process/function/procedure declarative regions ). It seems really odd that you can declare a package inside a process, but that means you cant then re-use the package in another process.
 

How do you get around getting the driving signal out of a clocked FSM without a 2-process design or manually duplicating the logic? The duplication isn't hard, but I've had too many issues with design updates that don't update the duplicated logic. And for some FSMs, the logic is complex -- small differences can be very hard to catch in sim or even normal testing.

for my #2, I use procedures a lot. I also use vim for marker-based code folding, so large sections of routine code just disappear. My goal is to make the core part of the logic as short and meaningful as possible. To that end, I would prefer to have a ramWrite(addr, value) procedure, which sets en, wr, addr, and din to suitable values. But if I have two or more BRAMs, I need to have multiple duplicate procedures where the only difference is the signal names. I'd like to be able to say:

Code:
procedure tagRamWr is ramWrTemplate(tagEn, tagWr, tagAddr, tagValue);

or something similar, not sure what syntax works best. That way I don't need to copy-paste the same declarations over and over again just to save a few lines in the core of the design. Really, having the ability to return procedures from functions in this manner makes this style of code possible.

my #3, really, just the example I gave. I have a really nice address decoder that works with base/high addresses. But I still have to do the allocation of registers for my design manually. It would be nice to be able to have all modules send "I use x control addresses" and to have the controller pass back the actual addresses.

my #4, probably. But there are so many times I want features like this to exist. I guess that would just be an addition to HDL and could already be done by adding custom attributes.


I actually rarely used access types and just looked up protected types. I think they sound like something I should use more often!
 

I get the feeling the way you are using the code is not how it was origionally envisaged, and probably not how 99% of engineers use it.
I still dont quite get your procedures - why cant you just use the same procedure twice? Why cant you just call the first procedure from a 2nd procedure? You know you can have signals as a procedure paramter?

procedure ram_wr is ( signal en : in std_logic; signal addr : in std_logic_vector; signal data : out std_logic_vector);

then you just wire up the signals you want actioned in the procedure?

Also, protected types - dont even think about using them for synthesis - they were never intended for that. They are for simulation only (same for access types)
 

I try to push the limits of the tools more than others.

The issue with ram_wr is that the impure members are missing. the real generic ram_wr would be called as "ram_wr(in_addr, in_value, out_actual_en, out_actual_wr, out_actual_addr, out_actual_value)". I want to set the impure aspects using the function so I can use it like "tag_ram_wr(in_addr, in_value)". Although you are correct, I could make an abstract procedure, then create smaller concrete procedures for each ram. Still, just being able to templatize the whole thing would be easier.

I've had similar issue with just wanting a pure boost::bind style function, where I could say in one line "this function is the same as this other function, but the second argument is passed in as a generic". To do this now mean a new function, which is a multi-line copy/paste.

I am also disappointed that protected types are non-synth. I actually was thinking about how I liked to use records and procedures -- something almost like C-style objects.
 

I really think you are alone with your problems.
All engineers I know have no problems with the language, and we get on fine making stuff with the code as it is.

I also think your code style would not be acceptable in most companies - other engineers just wouldnt understand it. It doesnt really lend itself to long term maintainability. It's clever, sure, but not really for long term support by multiple engineers.
 

Most of the issues raise here are with the synthesis tools, not the language. I propose that any executable construct in an HDL language is synthesizable given enough time, space, and money. It's just a matter of where synthesis tool vendors choose to spend their efforts that give them the best results. (And for many synthesis tool vendors, synthesis tools are not their main business; selling FPGA chips and development boards are).

#2 SystemVerilog has the let construct which is very similar to a lambda function.
#3 SV also has default port and procedure argument connections.
#4 Verilog already has an attribute mechanism that a synthesis tool could choose to use or preserve signals.
 

How do you get around getting the driving signal out of a clocked FSM without a 2-process design or manually duplicating the logic?
Personally, I don't recall a case where I need some concurrent output to be a function of the next state so what you're describing has never come up. Maybe your interfaces need some work to get rid of the situations you describe. The common command/wait or command/ack interface typically doesn't need the outputs to be a function of next state.

I don't get how you would have "the only difference is the signal names" when accessing two different BRAMs in a design. I would expect that the two BRAMs would show up at different addresses within the same address space so the same signals would be used. Instead what would be different is the constant address of the individual BRAMs. But maybe you're describing something else.

In my situation, registers/memories that are addressable by some external source have types defined for the detailed bitmap. Now when I want to read/write some port there is a read/write procedure defined that handles only that type. Since the type is known, the address to use is known so that value can be computed within the procedure. There are two layers to the read/write procedures: the lowest level has the full signal list and performs whatever the detailed read/write protocol requires but works strictly with the lowest common denominator of std_ulogic_vector or unsigned for the interface. The upper level simply passes in the value to be written (or to be read from); it has the code that calculates the constant address to be read/written and then converts between the register data type and std_ulogic_vector and then passed along to the lower level procedure. The upper level procedure is defined within process scope so that it can present the simple interface list (i.e. just the data on a write; no parameters for a read, it simply returns the typed data). So writes look like this: Write_Port(xyz_port); Reads look like this: xyz_port := Read_Port;

This one doesn't make any sense. Any block that has a set of registers must have an address input. Assuming that to be of type std_logic_vector or unsigned then 2**addr_inp'length will give you the address space for that entity. If instead you use type natural as the address input, then should also also have defined an upper limit to that address (i.e. addr_inp: natural range 0 to XXX). Wherever it is that XXX gets defined (which would most likely be in a package that defines the registers and the bitfields), would get used as well in the higher level entity. There would not be any reason to pass up the number of addresses from the individual module to a higher level module.

Kevin Jennings
 


Maybe this is a case of implementing Mealy machines as opposed to Moore machines. You can't combine the outputs and state logic in a single process FSM when you implement Mealy machines. Unless they are synchronous Mealy machines which are really sort of a Moore machine in disguise.
 


IIRC, the common place for this was when some action would take a cycle to complete and should be done when the FSM returns to idle (vs 1 cycle later).

I don't get how you would have "the only difference is the signal names" when accessing two different BRAMs in a design.
two independent BRAMs will have different signals connected to the ports. The way to write to either independent BRAM is the same with the only difference being the names of the signals connected to the ports.

This one doesn't make any sense. ... Wherever it is that XXX gets defined .... There would not be any reason to pass up the number of addresses from the individual module to a higher level module.
ah, sorry. I meant I had to do the assignment myself. My point was that if all modules passed the address space requirements up, it would be possible to assign addresses from a script. This feature was more of a toy idea. IIRC it would also be possible with a tcl script.
 

Status
Not open for further replies.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…