Filament – A Language for Fearless Hardware Design(filamenthdl.com) |
Filament – A Language for Fearless Hardware Design(filamenthdl.com) |
This may be the first HDL I've seen that attempts to move the needle on catching bugs at compile time. (I've worked with several engineers, on hardware bugs which turned out to be pipelining errors, who did not understand what I meant by "make this design error inexpressible.") I have several pages of notes on what I'd do differently if I designed my own HDL - the typical software engineer hubris - and this is the first language I've seen that starts to line up with what I was thinking.
Another perennial area where bugs crop up are when crossing clock and reset domains. The language ought to be able to make it so that you simply can't make many kinds of clock domain errors - trying to read a signal from the wrong clock domain shouldn't compile. Dedicated "unsafe" primitives from a stdlib should perform the crossing and the type conversion.
I’m all in favor of a better HDL. Verizon/SystemVerilog is loaded with completely non-obvious landmines. I’ve been doing this so long I forget they are there, but it’s pretty painful seeing someone new to the language step on them. But the alternative, VHDL has largely fallen out of favor in the US.
You would be hard pressed to find a more strongly typed language than VHDL, but damn is it verbose. None of the footguns, but you might get an RSA before you finish typing the code in. If you have ever given Ada a try, VHDL will look pretty familiar.
I know this may be a weird thing for software folks to think about, but writing HDL is a tiny part of digital design. In digital design, if done with discipline, writing the HDL is an almost mechanical process of translating the design. In a design that might take a year, writing the code might be 3 weeks.
Done without discipline you will spend all your time debugging. Wondering why it worked in the lab an hour ago, but after lunch nothing works and you won’t be able to make sense of it.
Understanding basic combinational logic, then sequential logic, followed by state machines (which are the bread and butter of digital design), followed by understanding IO timing and timing constraints (a brain damaged “language” to itself) will take you far.
Domain crossing isn’t so bad if you have those fundamentals.
Then you can spend time learning algorithms other more interesting things. Writing low power software accelerators for neural nets and signal processing.
You can go through all the gyrations of language design in the world, but the language isn’t the hard part. There is a huge amount of improvement to be done, no doubt. But digital design is not the language.
If you want to make the world of digital design a better place, more open, easier to break into, work on tools, not languages. I’d give a kidney for an open source timing diagrammer that could do simple setup and hold checks, create derived signals through Boolean combinations of other signals, and emulate a flop.
I’d do it, but I’ve tried and programming a gui is about the most painful thing I’ve done on a computer. So much work for so little payoff.
I say this as an FPGA engineer that started my career in software: This used to be the case in software too. But we now have such good tools with REPLs, automatic tests, IDE integrations, partial implementation possibility that it's simply way less inefficient to build software like that.
I feel like that is precisely where the current hardware ecosystem stands. VHDL/Verilog/SV are simply not good enough to use it as a design exploration tool.
Instead of saying: "We use HDLs just to put in the design which we do beforehand" it should be "HDLs are the central tool in digital design. Both during design exploration and implementation". I think this is what people want out of these "neo" HDLs. The way you are viewing it is as only as a straightforward drop-in replacement. That is not what it's about.
That's generally make take too, only I'd say they make some of the hard parts harder.
When you're getting into timing, power and area optimizations it's important to have a good mental mapping from the HDL you're writing to the circuitry (or FPGA configuration) being produced. For some optimizations you also need to smash through neat abstractions.
Often these new HDLs abstract away more from the circuitry so you have to carry more things in your head to think through these optimizations. They aim to work out the details for you but those details matter and when you only have indirect control over them can make things more difficult.
In software we're generally happy to accept a drop in performance or an increase in memory usage to gain powerful abstractions and increase developer productivity. In hardware this is often less acceptable (in particular if you're building high-performance CPU, GPU, AI accelerators etc).
From my brief look at the front page of the Filament website I do like the type system but from experience building real CPUs (I worked on the A55, A510 and E1 at arm, focussed on memory systems and now work on Ibex: https://github.com/lowrisc/ibex amongst other things) bugs and other issues around pipelining are often to do with stall conditions, I wonder how well it can handle that? Stall conditions were large and complex and also where you had to do lots of those abstraction busting optimisations to meet timing so it's critical you have a strong grasp of the critical paths being produced in the circuitry. My fear with Filament is it abstracts a bunch of that away.
Of course the entire hardware world isn't CPUs, there could well be areas of design this kind of language works well. In particular I can see it's useful for prototyping/design exploration type work.
I know what the statement means, but I fail to imagine any hardware construction that is automatically wrong, merely ones that don't do what someone wanted. The same exact construct and resulting behavior could be what someone else wanted.
Violating the nominal specs, or the nominal intended purpose of a part or circuit to get particular results is the oldest thing in the world.
Maybe some things are common enough that it's reasonable to make them difficult to express?
Ok, also especially if you can assert your intentions with labels that say high level things, then I guess I can see being able to flag some things that would break the intentions expressed by the labels.
Maybe I'm starting to imagine a little.
The timing aspect is super interesting, though I wonder if a compiler given an fpga and a program could optimize the hard parts on an fpga and run the rest on a risc V core.
I was interested in the use of Filament to implement an entire RISC V processor ("frisc") but the link [1] at the bottom of the readme is broken and some quick searches of both Github and the web turned up nothing. Does anyone know what's up?
I meant, in software programming, we usually program by either specifying the sequence or dependency.
In hardware, nothing runs sequentially, and signals propagate _with delay_. Everything happens at the same time, yet nothing run at the exact same moment. How could we express these chaos in for-loops and procedure look alikes?
Can we please stop with illegible text? Color: 333 indeed.
There is some confusion because the hardware can also be simulated, in a fairly straightforward way - you just need to model all those delays and run an event based simulator. Then the existing HDLs like (System)Verilog contain actual programming language constructs that are just there for use in simulation and can't actually be replicated in hardware.
The way I like to think of hardware is as a machine that takes a giant blob of state, applies a pure function to it, and when the clock goes from low to high it replaces the original state with the new one and the process starts again.
Hardware for loops are just fine if you think of a for loop as existing in space rather than time--effectively it is always fully unrolled.
> The way I like to think of hardware is as a machine that takes a giant blob of state, applies a pure function to it, and when the clock goes from low to high it replaces the original state with the new one and the process starts again.
That's the way hardware guys think of it, too. :)
In most HDLs you have two kinds of code, procedural and continuous code.
Your continuous code is wires which propagate signals and continuous logic (basically pure functions). Since there aren't any registers in this logic, that means you generally need to do timing analysis to make sure it never gets too long between any two given registers (or latches if you are clock stealing). In verilog you mostly do this via assign statements and module instantiations.
Your procedural code is register or latch based code. In verilog these are your always blocks. Here the code represents logic that occurs on every occurance of a given clock edge. The type of always block you use and which edge you specify changes exactly what happens but generally it means "this code runs during this interval every time the clock does a specific transition". Your inputs are generally going to be registers or latches and your outputs will be as well. Anything that happens must do so fast enough for the logic in between to reach a steady state and long enough for it to catch the edge of the register, edge of the edge triggered latch, or level of the level triggered latch and set the register or latch. If that sounds complicated it kinda is but generally you just let your timing analyzer figure it out and complain if stuff is too slow.
Now outside your normal procedural code there are some other ones you see occasionally. There are also initial blocks which only run once and repeat blocks which only run a set number of times but with both you can pause or wait for a condition before proceeding. And there are also forever blocks. In all three of these blocks you can specify timings and delays for things. The most common use for forever blocks for example is to declare a clock that runs at a specified frequency. And initial blocks are used to do setup (such as start the clock loop, set initial values, etc).
Now as to how you do loops and procedural logic? You use registers to break it up. That's what those always blocks are for. Your always block does a little bit of work each time and you use a state machine to codify where in the loop or procedural logic you are. In general FSMs (finite state machines) become your friend in HDL work very quickly.
> continuous logic
This is terminology I'm used to. Generally this is called "combinational logic" in my experience. Is this a language or regional difference?
The way you then write hardware is you define a series of blocks which are in the form "when this clock edge happens, update these variables in this way based on the previous value of variables in scope". It doesn't matter so much how you express this change, so long as the synthesis engine can map any given set of input variables to an output, which it will turn into digital logic. It is why verilog has odd constructs like "assign this value to this variable after the next time step". It's not unlike writing event-driven non-blocking code (without async or green threads).
Note at this point that the HDL written is saying nothing about delays: by running it in simulation, everything will complete in time for the next clock cycle. And the synthesiser will happily produce a circuit which the place and router can't actually make fast enough for the next cycle. That's where two other parts come in: firstly there's timing analysis which takes the low-level results of the place-and-route and basically tries to work out the fastest and slowest a signal can pass from one register output to another register input, and how that compares to the clocks to those registers (usually it'll be the same clock). That is generally what will tell you if what you've written won't work in some situation or another. And secondly, the tool can also take that low-level compiler outout, and give you a kind of "decompiled" version in your HDL, which doesn't bear much resemblance to what you put in but does include all the estimated delays, so you can simulate (much more slowly) what the timings will actually look like.
The upshot of which us that writing an HDL generally involves thinking about what the hardware would look like, writing some code that simulates what that hardware would be doing, running it through a slow build cycle, looking at what hardware was actually generated and working put why it's failing timing, thinking about how to change the hardware and then changing the code to get the sythesis tool to spit out the right version of that. You don't tend to get super high levels of abstraction in a given execution path: more the abstraction is in how you connect those parallel blocks together.
Assuming you meant to also include 'verification', then I can agree somewhat.
As an ASIC designer, you usually get just one stab at a hardware implementation, there's no 0-day point release. It's got to work so you spend the minimum time typing it in. Then you devote the maximum time to verifying it. I think some of the very poor quality s/w we are all subject to is partly due to a false sense of security that sw tooling provides. I expect fully working and efficient hardware, I'd like that from my software too, but in reality I rarely get that. The idea of applying more software dev processes/principles to hardware is a bit frightening to me.
This project involves static verification of one part of hardware design, namely the timing properties of pipelines and sequential circuits.
This is not all of verification (which is a huge issue in modern semiconductor design) or even of high-level design verification, but it probably helps.