MJ Logic Design
We begin the design process by working with our client’s architecture team to
define the requirements. This often involves some back-and-forth to quickly
consider requirement options and complexity implications. Our goal is to then
produce a short (1-3 pages) proposal that provides an overview of the basic
features and performance, in addition to a high-level block diagram and
programmer’s model. Once the proposal is approved, we then move into the
sub-system or block micro-architecture design phase, where our goal is to
produce a structured design that requires the least amount of complexity in
order to meet the requirements. A simple test that we use to gauge our
success is to ask ourselves the following questions:
When we can answer yes to both of these questions, we know that we have a
- Is the design easy and/or straightforward to explain?
- Can we tell a good story – i.e., can we walk a packet or transaction
through the design without any obvious holes?
design that we can document, implement, and debug per the resulting
For individual blocks, we often update the original proposal so that it can
simply be pasted into a sub-system/chip specification. For larger sub-
systems, we usually generate a full standalone specification, complete with
individual sections describing requirements, theory of operation, micro-
architecture, programmer’s model, external interfaces, and open issues.
Both our initial micro-architecture and subsequent implementation in Verilog
RTL take into account the full set of requirements. We don’t code for the
simple cases first in order to pass a few packets/transactions quickly, only to
make a second pass later to handle the more complex cases. Of course,
there are always exceptions, such as error cases that may or may not need to
be handled, but in general we feel the highest quality design is achieved by
initially coding with the full set of requirements in mind. However, given that
our designs are very structured, as requirements change, our designs are
usually easy to modify.
Following simulation, debug, and the initial hand-off of our design to the
verification team (discussed in next section), we typically move into synthesis
and work to quickly achieve timing closure. In recent years, most of our ASIC
designs have operated in the 250-350 MHz region, and therefore we have
established a good feel for how many logic levels can be implemented per
pipe stage at these frequencies.
We spend a significant portion of our time in verification, either verifying our
own designs or those of others. We verify at both the unit- and chip-level,
building environments and creating regression tests. Regardless of the level
(unit or chip), our verification philosophy is to put the majority of the complexity
in the environment so that the tests can be relatively simple and operate at a
high level of abstraction. Since our early days in networking back in the mid
1990’s, we’ve been building smart generator/scoreboard/checker
environments to perform both directed and constrained-random tests.
Depending on the customer, we either create the environment ourselves or
work within an existing environment. The environments have ranged from all-
Verilog, to a mixture of Verilog and SystemVerilog, to a full class-based Vera
environment. Regardless of the language, we subscribe to the high-level
verification principles as described in VMM/RVM/etc. Our environments are
capable of randomizing all aspects of packet/transaction-based streams, so
that our tests focus on setting up the desired scenarios and letting the
environment do the rest (i.e., generate stimulus and check the results).
As very experienced ASIC designers, we bring a unique and very valuable set
of skills to design verification. Not only are we able to create random
environments that are effective at stimulating all types of functional and stress-
related bugs, we also have excellent block- and chip-level debugging skills.
In other words, we know how to both stimulate and track down the most
difficult types of bugs in designs. This applies to the designs that we
generate, as well as the designs of others. Furthermore, on projects where
we were responsible for chip-level verification of a design generated by
others, we were able to trace bugs into the offending hardware block, and in
most cases recommend a fix to the responsible design engineer. This saved
the designers a tremendous amount of time and allowed them to remain
focused on feature completion/enhancement. In some cases, we even
assumed responsibility for implementing RTL fixes in blocks without defined
Finally, when working as designers with supporting verification teams, we
know that good teamwork is the key to preventing chip bugs. The following are
things we do as designers to foster this teamwork and help the verification
team do their job:
- Keeping design specifications clear and up-to-date.
- Providing insights into critical testing areas, corner cases, stalling
- Thoroughly evaluating all test plans.
- Reviewing code coverage results.
- Providing timely and thorough responses to all bug submissions.
RTL and Testbench Code Library
Having been in consulting for a number of years, we have developed an
extensive library of re-usable Verilog RTL modules, RTL code segments, and
testbench code. Obviously, both ethically and contractually, we do not re-use
designs from one client to another, so what our library consists of is smaller,
standard, proven circuits that we find are common to most designs. The use
of these proven circuits saves our clients both time and money, reduces bugs,
and contributes to our overall structured design methodology. Examples of the
RTL code in our library include: various types of synchronous/asynchronous
FIFO controllers, FIFO prefetch circuits, and register files. In terms of
testbench code, we are often able to quickly adapt existing packet or
transaction generators/checkers, along with a variety of bus functional models
to quickly realize the unit-level debug environments mentioned in the
Verification section above.