Thursday, 11 July 2013

What's Bad about Bluespec System Verilog (part 3)

This is the final part of my series on the subject of Bluespec System Verilog, which started with the good aspects of the language and progressed through various disadvantages. This part wraps it all up by talking about the interface between Verilog and BSV.

This interface is important in two cases. Firstly, when we want to do something that can be done easily in Verilog (or VHDL), but not in BSV. And secondly, when we are debugging a design.

The first case frequently came up during the development of Blueshell, in order to implement the top-level of the design (i.e. connections to I/O ports) and in order to implement connections to pre-built components. For instance, Blueshell has a Microblaze CPU tile which contains a Microblaze CPU. I didn't reimplement Microblaze in BSV. This might be an interesting exercise, but it would also be extremely time-consuming. Instead, I just created a Microblaze CPU project in Xilinx Platform Studio (XPS) and linked it to BSV via a Verilog wrapper, thus reusing the Xilinx IP core.

A similar task was needed to connect an external memory controller for the DDR memory on boards such as Atlys and ML605. DDR memory controllers are extraordinarily difficult to write because of the need to drive the physical memory interface with very precise timing. The controller design tends to be specialised to a particular FPGA board. If DDR memory is required, the only sensible approach is to reuse an existing IP core. That's exactly what Blueshell does - using an XPS project linked to BSV via a wrapper.

Unfortunately, BSV's capability for interfacing to VHDL/Verilog is limited. The interfaces are written as "BVI" components, which look like BSV modules, but are actually wrappers for Verilog code. The Verilog signals are translated into a BSV representation where they appear as methods. As noted earlier, BSV methods represent inputs and outputs from a module. Methods have no equivalent in Verilog, so they are represented by a group of wires. There are data lines for inputs and outputs (parameters are inputs, return values are outputs), an enable signal and a ready signal.

Verilog signals can only be brought into BSV if they fit this paradigm, i.e. if signals from the VHDL interface can be identified as ready, enable and data lines. Sometimes the ready/enable/data signals map directly to bus lines. But if they don't, BSV will not help you, and you must to write a shim layer to make up the difference.

BSV's method connections involve assumptions that cannot be changed. For a simple, superficial example, BSV ready and enable signals are active high, and reset signals are active low. If your VHDL/Verilog component has an active high reset, or an active low ready signal, then tough! You have to write a shim layer in VHDL or Verilog just to flip the polarity of that signal.

But the problem goes deeper than that, because BSV also involves assumptions about timing and the semantics of signals. In the case of Microblaze, the local memory bus (LMB) signals do not match the BSV paradigm. Though there are signals that act as "enable" ("Strobe") and "ready" ("Wait"), they do not match the BSV meaning. Wait is asserted if a request has been acknowledged but the data is not yet ready, while the other connections are only valid for a single clock cycle.

The BSV/Microblaze shim was extremely tricky to write, and after a few attempts, I split it between a Verilog component and a BSV component. The Verilog component controls the CPU's "Wait" signal, and represents the outgoing/incoming data lines as two BVI methods. The BSV parts of the shim must "call" these methods every clock cycle in order to avoid the possibility of losing data.

The major challenges here are correctness and speed. Requests have to be served within one clock cycle if possible (a cache hit). This would be easy in a pure BSV design, but not in a mixed BSV/Verilog design.

In my experience, any interface between BSV and Verilog is likely to be a source of bugs. I spent a lot of time in the logic simulator trying to locate and fix timing errors in the Microblaze interface and in similar BSV to Verilog interfaces within Blueshell.

These interfaces are needed when interfacing to IP cores, such as Microblaze and a DDR controller. They're also needed when using low-level FPGA components such as block RAMs. In each case, you are lucky if the module interface matches up to a BSV method. Most likely, a Verilog or BSV shim will be required, due to the lack of flexibility in BVI specifications.

I would like to see an improvement of BVI specifications which would allow the use of "inline" Verilog, similar to inline assembly code. This would make it possible to write Verilog shim code as part of the BVI specification, without needing an additional BSV or Verilog module. I would also like to be able to include BSV rules and methods within BVI specifications, making it possible to include BSV shim code as well.

This might also be an opportunity to revise the confusing mechanism for specifying scheduling conflicts between BVI methods, which is used to declare which methods cannot be "called" at the same time.

Verilog/BSV interfaces are also likely to exist at the top level of a design, where the HDL code meets the FPGA's I/O pins. It is possible to write a pure BSV top level. For instance, you can declare that the top level methods are (* always_ready *) and (* always_enabled *) and use (* port=".." * ) to rename the connections to match FPGA I/O pin names. But I wanted the possibility of other components within the top level, such as clock buffers and DDR memory controllers, so all Blueshell projects have a top level written in VHDL.

BSV makes some effort to hide Verilog details from the designer, but Verilog never goes away, and it's always important. It turns up in "BVI" interfaces, and at the top level, but it's inescapable in one other place - when you're debugging.

The BSV compiler software includes a logic simulator, but it only simulates BSV itself. VHDL/Verilog components aren't included in the simulation - nor are XPS projects. This was not useful to me, so I was forced instead to use the Xilinx logic simulator, ISim.

ISim sees only VHDL/Verilog code, not BSV, and so debugging a BSV design is rather like debugging in assembly code without any tool support to help you understand the relationship between the VHDL/Verilog signals and BSV methods and registers. It's better than nothing, but it's hard work.

Sometimes, BSV helps you by naming Verilog signals after their BSV equivalents. Methods are named like this, as are registers, and the conditions for rules:
assign server_response_get = ...;
assign RDY_server_response_get = ...;
assign WILL_FIRE_RL_writing = ...;
Signals generated for a state machine may be given names derived from their location in the source file:
WILL_FIRE_RL_fetcher_fsm_action_l54c9
(Line 54 column 9 in this case.) However, signal names are not always readable. Sometimes they are named after their expressions:
m_mbl0_dc_address_97_BITS_31_TO_4_98_EQ_INV_m__ETC___d919
and sometimes the compiler gives up entirely and produces a meaningless name, such as "x__h22385".

Naming conventions become unclear when modules are instantiated within loops. BSV allows you to build arrays of components recursively and iteratively, and if this feature is used, then it is very hard to relate any component name to the exact stage of the iteration or recursion that produced it.

This is unfortunate, because in ISim, you only see these names. When debugging your design, you have to manually match them to the BSV code.

It's a problem, and it slows down debugging. A sort of solution has already been implemented in BSV, as the designer can declare "Probes", which are write-only "registers" that appear only during simulation. Probes can be named in any way that is helpful to the designer, and are easily identified in ISim.

Other solutions are not easily suggested. They would rely on some ability for Verilog to carry "debugging symbols" to relate Verilog signals to BSV code. Verilog doesn't have this ability, though it might be possible to place BSV code in Verilog comments near to the generated signals.

The real solution would be direct support for BSV inside ISim, or another logic simulator capable of understanding BSV, VHDL and Verilog together.

Perhaps, as this interesting language is improved, it will eventually be directly supported by FPGA tools rather than supported via Verilog. Maybe we will eventually get pre-built BSV interfaces for Xilinx IP cores including Microblaze and XPS projects, and integrated BSV support within ISim and the synthesiser. Perhaps there is an opportunity here for other FPGA vendors - a way to get ahead of Xilinx.

The proprietary nature of BSV, which is only produced by Bluespec Inc., has been named as a reason not to use it. This is a valid criticism, but many good software languages started off as proprietary standards, and this did not prevent them later becoming open standards or inspiring the creation of derived languages that were open.

Studying the strengths and weaknesses of BSV is the only way to find where improvements are possible. I hope that by writing up my experiences of the past year, working on the Blueshell project, I have given some clues as to the sorts of improvement that might be possible and desirable.

In part 1, I recommended that the important notion of priority, applicable only to rules, should be extended to methods in some way. In part 2, I recommended simplifying the awkward formal type system with assertions that are checked at compile time. In part 3, I recommended improving the interface to Verilog code, and I asked for better simulation too.

But this short list is nothing in comparison to the list of features I would want for Verilog or VHDL. I cannot overstate the improvement made by BSV over the classical HDLs and I strongly recommend BSV for hardware design projects. I doubt that anything as complex as Blueshell could have been written within a year without it.