OSVVM – Thinking beyond constrained random

osvvm_logo_thumb What is OSVVM?

OSVVM stands for "Open Source VHDL Verification Methodology". OSVVM is a set of VHDL packages, initially developed by Jim Lewis of Synthworks. OSVVM helps you adopt modern constrained random verification techniques using VHDL.

Constraint random verification approach :

In testbenches, we generally want one each of a large set of test cases (transactions and/or sequences). Uniform randomization does not generate one each. Instead it has a significant amount of repetition. In general, uniform randomization takes O(N*LogN) randomizations to generate N unique test cases. As a result, it repeats Log N test cases. Even for small numbers such 64 test cases, constrained random will generate more than 4X more test cases than needed - actual results will vary with the randomization seed. Constrained random comes with this fundamental problem. Randomization is intended to be uniform over time. However constraint random verification has a number of benefits:

  • If you simulate longer, you generate more test vectors.
  • You may find bugs due to unexpected combinations of inputs, or extreme input values. With directed testing, it is all too easy just to test what you expect to happen, rather than trying to test what you don't expect to happen.
  • Once you have developed an automated test, it can still be used for directed testing.

Still what we need is an approach that only requires O(N) randomizations to generate N unique test cases. Generally these approaches are referred to as being Intelligent Testbenches. Indeed there are some tools out there that handle this. However, when we use a tool based approach we end up with a vendor specific solution. This removes one of the major benefits of a programming language based approach - encounter a issue (pricing or functionality) with one vendor and you can easily switch to another.

What we really need is a methodology for Intelligent Testbenches that is based on a standard language and works on numerous vendor tools.

OSVVM :

VHDL's Open Source VHDL Verification Methodology (OSVVM). OSVVM's methodology leverages the functional coverage you must write when you are using any randomization based approach. Intelligent Coverage™, the main randomization methodology for OSVVM, randomly selects a hole in the coverage and passes this to the stimulus generation process. The stimulus generation process uses this information, perhaps refines it using any methodology (directed, algorithmic, constrained random or file based), and then generates one or more transactions to accomplish generate the item that needs covered.

OSVVM can be used in your current VHDL testbench, in part or in whole as needed.  It allows mixing of our signature “Intelligent Coverage” methodology with other verification methodologies, such as directed, algorithmic, file based, and constrained random. Don’t throw out your existing VHDL testbench or testbench models, re-use them.

There is no new language to learn. There are no specialized “OO” approaches – just plain old VHDL entities and architectures. As a result, it is accessible to RTL designers. In fact, it is our goal to make our testbenches readable to verification (testbench), design (RTL), system, and software engineers.

OSVVM works with any VHDL testbench and is particularly effective when coupled with a transaction based testbench. For us, VHDL and OSVVM are the step beyond constrained random and SystemVerilog. Maybe it is time we update VHDL's acronym to mean Verification and Hardware Design Language.

Intelligent Coverage™ Methodology :

Verification starts with a test plan that identifies all items in a design that need to be tested.  OSVVM, like other advanced methodologies, uses functional coverage to observe conditions on interfaces and within the design to validate that the items identified in the test plan have occurred.  As such, functional coverage helps determine when testing is done.

Unlike other methodologies, in OSVVM’s Intelligent Coverage methodology,  functional coverage is the prime directive – it is where we start our process.  Intelligent Coverage is done in the following steps.

  • Write a high fidelity functional coverage (FC) model
  • Randomly select a hole in the functional coverage 
  • Refine the initial randomization with sequential code 
  • Apply the refined sequence (one or more transactions) 
  • Observe Coverage

The key point of Intelligent Coverage is that we randomize using the functional coverage. Then, if necessary, we refine the randomization using sequential code and any sequence generation method, including constrained random, algorithmic, directed, or file reading methods.

OSVVM is a Low Cost Solution :

The packages are free. OSVVM works on regular VHDL simulators (such as Mentor’s ModelSim and Aldec’s Active-HDL) without additional licenses. The only special language support required is VHDL-2002 protected types and VHDL-2008 type integer_vector (for older simulators, we have a work around for this).

To learn more about OSVVM, see:


OSVVM is an open source VHDL library that is free to use (no license fees) and works with any simulator that supports VHDL-2008 (or VHDL-2002 with a little work).
What is currently in the OSVVM library is only the beginning. Over time, I will be releasing our generic scoreboard package, memory modeling package, and others.

Intel funding to develop printer for blind

subhum-banerjee-and-braille-printerA 13-year-old Indian-origin boy has received a huge investment from Intel for developing a low-cost printer for the blind, making him the youngest tech entrepreneur funded by a venture capital firm.

Shubham Banerjee, CEO of the Braille printer maker Braigo Labs, had closed an early round funding with Intel Capital, the company's venture capital arm, last month to develop a prototype of low-cost Braille printer.

But to attend the event, Banerjee had to take the day off from middle school. That’s because he’s just 13 years old — making him, quite possibly, the youngest recipient of venture capital in Silicon Valley history. (He’s definitely the youngest to receive an investment from Intel Capital.)

“I would like all of us to get together and help the visually impaired, because people have been taking advantage of them for a long time,” Banerjee said. “So I would like that to stop.”

By “taking advantage,” Banerjee is referring to the high price of Braille printers today, usually above $2,000. By contrast, Braigo Labs plans to bring its printer to market for less than $500.

Banerjee has invented a new technology that will facilitate this price cut. Patent applications are still pending, so he wouldn’t divulge any of the details. But the technology could also be used to create a dynamic Braille display — something that shows one line of text at a time by pushing small, physical pixels up and down, and which currently costs $6,500, according to Braigo advisor Henry Wedler, who is blind.

Banerjee also figures that volume production will help keep the price low. Currently, Braille printers cost so much because the demand is low, so current manufacturers need to set a high price in order to recoup their costs.

“The truth is that demand is low in the U.S.,” Banerjee told me. But, he added, if you brought the price low enough there would be huge demand outside the U.S.

Banerjee built the version version of his Lego Braille printer for a science fair. He didn’t know anything about Braille beforehand. In fact, he’d asked his parents how blind people read, he said onstage, and they were too busy to answer. “Go Google it,” he said they told him, so he did.

After learning about Braille, he came up with the idea to make a Braille printer. He showed it at his school’s science fair, then later entered it into the Synopsys Science & Technology Championship, where he won first prize, which included a big trophy and a $500 check.

After that, he started getting a lot of attention on his Facebook page. People kept asking him if they could buy one, he said, which led to the idea of creating a company.

Lego was just for the first prototype, by the way: Future versions will be made with more traditional materials.

So how did Intel come to invest in such a young inventor? His father, Niloy, works for Intel — but that’s not exactly how it happened, according to Niloy.

After working with the beta version of Intel Edison (the chip company’s tiny embeddable microprocessor) at a summer camp, Banerjee’s project came to the attention of Intel, which invited him to show off his printer at the Intel Developer Forum. After appearing at IDF, Intel Capital came calling.

Young Banerjee seems composed in front of crowds, which should serve him well. (That’s not surprising, given that Braigo’s website touts coverage on everything from BoingBoing and SlashGear to CNN and NPR.) When asked onstage, in front of 1,000 entrepreneurs, investors, and Intel employees, how he knew that the printer worked even though he doesn’t read Braille, Banerjee answered immediately, “I Googled it.” The crowd laughed.

“I’m happy that I live in Silicon Valley,” Banerjee said. “So many smart people.”

UVM - Driver

The driver is a block whose role is to interact with the DUT. The driver pulls transactions from the sequencer and sends them repetitively to the signal-level interface. This interaction will be observed and evaluated by another block, the monitor, and as a result, the driver’s functionality should only be limited to send the necessary data to the DUT.

In order to interact with our adder, the driver will execute the following operations: control the en_i signal, send the transactions pulled from the sequencer to the DUT inputs and wait for the adder to finish the operation.

So, we are going to follow these steps:

  1. Derive the driver class from the uvm_driver base class
  2. Connect the driver to the signal interface
  3. Get the item data from the sequencer, drive it to the interface and wait for the DUT execution
  4. Add UVM macros

In Code 5.1 you can find the base code pattern which is going to be used in our driver.

class simpleadder_driver extends uvm_driver#(simpleadder_transaction);
`uvm_component_utils(simpleadder_driver)   //Interface declaration
protected virtual simpleadder_if vif;   function new(string name, uvm_component parent);
super.new(name, parent);
endfunction: new   function void build_phase(uvm_phase phase);
super.build_phase(phase);
void'(uvm_resource_db#(virtual simpleadder_if)::read_by_name(.scope("ifs"), .name("simpleadder_if"), .val(vif)));
endfunction: build_phase   task run_phase(uvm_phase phase);
//Our code here
endtask: run_phase
endclass: simpleadder_driver
Code 5.1 – Driver component – simpleadder_driver.sv

The code might look complex already but what it’s represented it’s the usual code patterns from UVM. We are going to focus mainly on the run_phase() task which is where the behaviour of the driver will be stated. But before that, a simple explanation of the existing lines will be given:


  • Line 1 derives a class named simpleadder_driver from the UVM class uvm_driver. The #(simpleadder_transaction) is a SystemVerilog parameter and it represents the data type that it will be retrieved from the sequencer.


  • Line 2 refers to the UVM utilities macro explained on chapter 2.


  • Lines 7 to 9 are the class constructor.


  • Line 11 starts the build phase of the class, this phase is executed before the run phase.


  • Line 13 gets the interface from the factory database. This is the same interface we instantiated earlier in the top block.


  • Line 16 is the run phase, where the code of the driver will be executed.

Now that the driver class was explained, you might be wondering: “What exactly should I write in the run phase?”

Consulting the state machine from the chapter 1, we can see that the DUT waits for the signal en_i to be triggered before listening to the ina and inb inputs, so we need to emulate the states 0 and 1. Although we don’t intend to sample  the output of the DUT with the driver, we still need to respect it, which means, before we send another sequence, we need to wait for the DUT to output the result.

To sum up, in the run phase the following actions must be taken into account:



  1. Get a sequence item


  2. Control the en_i signal


  3. Drive the sequence item to the bus


  4. Wait a few cycles for a possible DUT response and tell the sequencer to send the next sequence item

The driver will end its operation the moment the sequencer stops sending transactions. This is done automatically by the UVM API, so the designer doesn’t need to to worry with this kind of details.

In order to write the driver, it’s easier to implement the code directly as a normal testbench and observe its behaviour through waveforms. As a result, in the next subchapter (chapter 5.1), the driver will first be implemented as a normal testbench and then we will reuse the code to implement the run phase (chapter 5.2).

Chapter 5.1 – Creating the driver as a normal testbench


For our normal testbench we will use regular Verilog code. We will need two things: generate the clock and idesginate an end for the simulation. A simulation of 30 clock cycles was defined for this testbench.

The code is represented in Code 5.2.

//Generates clock
initial begin
#20;
forever #20 clk = ! clk;
end   //Stops testbench after 30 clock cyles
always@(posedge clk)
begin
counter_finish = counter_finish + 1;   if(counter_finish == 30) $finish;
end
Code 5.2 – Clock generation for the normal testbench
The behaviour of the driver follows in Code 5.3.
//Driver
always@(posedge clk)
begin
//State 0: Drives the signal en_o
if(counter_drv==0)
begin
en_i = 1'b1;
state_drv = 1;
end   if(counter_drv==1)
begin
en_i = 1'b0;
end   case(state_drv)
//State 1: Transmits the two inputs ina and inb
1: begin
ina = tx_ina[1];
inb = tx_inb[1];   tx_ina = tx_ina << 1;
tx_inb = tx_inb << 1;   counter_drv = counter_drv + 1;
if(counter_drv==2) state_drv = 2;
end   //State 2: Waits for the DUT to respond
2: begin
ina = 1'b0;
inb = 1'b0;
counter_drv = counter_drv + 1;   //After the supposed response, the TB starts over
if(counter_drv==6)
begin
counter_drv = 0;
state_drv = 0;   //Restores the values of ina and inb
//to send again to the DUT
tx_ina <= 2'b11;
tx_inb = 2'b10;
end
end
endcase
end
Code 5.3 – Part of the driver

For this testbench, we are sending the values of tx_ina and  tx_inb to the DUT, they are defined in the beginning of the testbench (you can see the complete code attached to this guide).

We are sending the same value multiple times to see how the driver behaves by sending consecutive transactions.

After the execution of the Makefile, a file named simpleadder.dump will be created by VCS. To see the waveforms of the simulation, we just need to open it with DVE.

The waveform for the driver is represented on Figure 5.1.

ch6-tb_driver_waveform

It’s possible to see that the driver is working as expected: it drives the signal en_i on and off as well the DUT inputs ina and inb and it waits for a response of the DUT before sending the transaction again.

Chapter 5.2 – Implementing the UVM driver

After we have verified that our driver behaves as expected, we are ready to move the code into the run phase as seen in Code 5.4.

virtual task drive();
simpleadder_transaction sa_tx;
integer counter = 0, state = 0;
vif.sig_ina = 0'b0;
vif.sig_inb = 0'b0;
vif.sig_en_i = 1'b0;   forever begin
if(counter==0) begin
//Gets a transaction from the sequencer and
//stores it in the variable 'sa_tx'
seq_item_port.get_next_item(sa_tx);
end   @(posedge vif.sig_clock)
begin
if(counter==0) begin
vif.sig_en_i = 1'b1;
state = 1;
end   if(counter==1) begin
vif.sig_en_i = 1'b0;
end   case(state)
1: begin
vif.sig_ina = sa_tx.ina[1];
vif.sig_inb = sa_tx.inb[1];   sa_tx.ina = sa_tx.ina &lt;&lt; 1;
sa_tx.inb = sa_tx.inb &lt;&lt; 1;   counter = counter + 1;
if(counter==2) state = 2;
end   2: begin
vif.sig_ina = 1'b0;
vif.sig_inb = 1'b0;
counter = counter + 1;   if(counter==6) begin
counter = 0;
state = 0;   //Informs the sequencer that the
//current operation with
//the transaction was finished
seq_item_port.item_done();
end
end
endcase
end
end
endtask: drive
Code 5.4 - Task for the run_phase()

The ports of the DUT are acessed through the virtual interface with vif.<signal> as can be seen in lines 4 to 6.

Lines 12 and 50 use a special variable from UVM, the seq_item_port to communicate with the sequencer. The driver calls the method get_next_item() to get a new transaction and once the operation is finished with the current transaction, it calls the method item_done(). If the driver calls get_next_item() but the sequencer doesn’t have any transactions left to transmit, the current task returns.

This variable is actually a UVM port and it connects to the export from the sequencer named seq_item_export. The connection is made by an upper class, in our case, the agent. Ports and exports are going to be further explained in chapter 6.0.1.

This concludes our driver, the full code for the driver can be found in the filesimpleadder_driver.sv. In Figure 5.2, the state of the verification environment with the driver can be seen.

ch6-uvm_tb_simpleadder_driver

Figure 5.2 – State of the verification environment with the driver

UVM - Sequences and sequencers

The first step in verifying a RTL design is defining what kind of data should be sent to the DUT. While the driver deals with signal activities at the bit level, it doesn’t make sense to keep this level of abstraction as we move away from the DUT, so the concept of transaction was created.

A transaction is a class object, usually extended from uvm_transaction or uvm_sequence_item classes, which includes the information needed to model the communication between two or more components.

Transactions are the smallest data transfers that can be executed in a verification model. They can include variables, constraints and even methods for operating on themselves. Due to their high abstraction level, they aren’t aware of the communication protocol between the components, so they can be reused and extended for different kind of tests if correctly programmed.

An example of a transaction could be an object that would model the communication bus of a master-slave topology. It could include two variables: the address of the device and the data to be transmitted to that device. The transaction would randomize these two variables and the verification environment would make sure that the variables would assume all possible and valid values to cover all combinations.

In order to drive a stimulus into the DUT, a driver component converts transactions into pin wiggles, while a monitor component performs the reverse operation, converting pin wiggles into transactions.

After a basic transaction has been specified, the verification environment will need to generate a collection of them and get them ready to be sent to the driver. This is a job for the sequence. Sequences are an ordered collection of transactions, they shape transactions to our needs and generate as many as we want. This means if we want to test just a specific set of addresses in a master-slave communication topology, we could restrict the randomization to that set of values instead of wasting simulation time in invalid values.

Sequences are extended from uvm_sequence and their main job is generating multiple transactions. After generating those transactions, there is another class that takes them to the driver: the sequencer. The code for the sequencer is usually very simple and in simple environments, the default class from UVM is enough to cover most of the cases.

A representation of this operation is shown in Figure 4.1.

ch5-uvm_tb_sequence

Figure 4.1 - Relation between a sequence, a sequencer and a driver

The sequence englobes a group of transactions and the sequencer takes a transaction from the sequence and takes it to the driver.

To test our DUT we are going to define a simple transaction, extended fromuvm_sequence_item. It will include the following variables:

rand bit[1:0] ina
rand bit[1:0] inb
bit[2:0] out

The variables ina and inb are going to be random values to be driven to the inputs of the DUT and the variable out is going to store the result. The code for the transaction is represented in Code 4.1.

class simpleadder_transaction extends uvm_sequence_item;
     rand bit[1:0] ina;
     rand bit[1:0] inb;
     bit[2:0] out;
 
     function new(string name = "");
          super.new(name);
     endfunction: new
 
     `uvm_object_utils_begin(simpleadder_transaction)
     `uvm_field_int(ina, UVM_ALL_ON)
     `uvm_field_int(inb, UVM_ALL_ON)
     `uvm_field_int(out, UVM_ALL_ON)
     `uvm_object_utils_end
endclass: simpleadder_transaction
Code 4.1 – Transaction for the simpleadder

An explanation of the code will follow:

  • Lines 2 and 3 declare the variables for both inputs. The rand keyword asks the compiler to generate and store random values in these variables.
  • Lines 6 to 8 include the typical class constructor.
  • Lines 10 to 14 include the typical UVM macros.

These few lines of code define the information that is going to be exchanged between the DUT and the testbench.

To demonstrate the reuse capabilities of UVM, let’s imagine a situation where we would want to test a similar adder with a third input, a port named inc.

Instead of rewriting a different transaction to include a variable for this port, it would be easier just to extend the previous class to support the new input.

It’s possible to see an example in Code 5.2.

class simpleadder_transaction_3inputs extends simpleadder_transaction;
     rand bit[1:0] inc;
 
     function new(string name = "");
          super.new(name);
     endfunction: new
 
     `uvm_object_utils_begin(simpleadder_transaction_3inputs)
     `uvm_field_int(inc, UVM_ALL_ON)
     `uvm_object_utils_end
endclass: simpleadder_transaction_3inputs
Code 5.2 – Extension of the previous transaction

As a result of the class simpleadder_transaction_3inputs being an extension of  simpleadder_transaction, we didn’t need to declare again the other variables. While in small examples, like this one, this might not look like something useful, for bigger verification environments, it might save a lot of work.

Sequence

Now that we have a transaction, the next step is to create a sequence.

The code for the sequencer can be found in Code 5.3

class simpleadder_sequence extends uvm_sequence#(simpleadder_transaction);
     `uvm_object_utils(simpleadder_sequence)
 
     function new(string name = "");
          super.new(name);
     endfunction: new
 
     task body();
          simpleadder_transaction sa_tx;
 
          repeat(15) begin
               sa_tx = simpleadder_transaction::type_id::create(...
 
               start_item(sa_tx);
                    assert(sa_tx.randomize());
               finish_item(sa_tx);
          end
     endtask: body
endclass: simpleadder_sequence
Code 5.3 - Code for the sequencer

An explanation of the code will follow:

  • Line 8 starts the task body(), which is the main task of a sequence
  • Line 11 starts a cycle in order to generate 15 transactions
  • Line 12 initializes a blank transaction
  • Line 14 is a call that blocks until the driver accesses the transaction being created
  • Line 15 triggers the rand keyword of the transaction and randomizes the variables of the transaction to be sent to the driver
  • Line 16 is another blocking call which blocks until the driver has completed the operation for the current transaction

Sequencer

The only thing missing is the sequencer. The sequence will be extended from the class uvm_sequencer and it will be responsible for sending the sequences to the driver. The sequencer gets extended from uvm_sequencer. The code can be seen on Code 5.4.

typedef uvm_sequencer#(simpleadder_transaction) simpleadder_sequencer;
Code 5.4 – Extension of the previous transaction

The code for the sequencer is very simple, this line will tell UVM to create a basic sequencer with the default API because we don’t need to add anything else.

So, right now our environment has the following structure:

 

ch5-uvm_tb_simpleadder_sequencer

Figure 4.2 – State of the verification environment after the sequencer

You might have noticed two things missing:

  • How does the sequence connects to the sequencer?
  • How does the sequencer connects to the driver

The connection between the sequence and the sequencer is made by the test block, we will come to this later on chapter 10, and the connection between the sequencer and the driver will be explained on chapter 7.

For more information about transactions and sequences, you can consult:

UVM–Top Block

In a normal project, the development of the DUT is done separately from the development of the testbench, so there are two components that connects both of them:

  • The top block of the testbench
  • A virtual interface

The top block will create instances of the DUT and of the testbench and the virtual interface will act as a bridge between them.

The interface is a module that holds all the signals of the DUT. The monitor, the driver and the DUT are all going to be connected to this module.

The code for the interface can be seen in Code 3.1.

interface simpleadder_if;
     logic    sig_clock;
     logic    sig_ina;
     logic    sig_inb;
     logic    sig_en_i;
     logic    sig_out;
     logic    sig_en_o;
endinterface: simpleadder_ifCode 3.1: Interface module – simpleadder_if.sv

After we have an interface, we will need the top block. This block will be a normal SystemVerilog module and it will be responsible for:

  • Connecting the DUT to the test class, using the interface defined before.
  • Generating the clock for the DUT.
  • Registering the interface in the UVM factory. This is necessary in order to pass this interface to all other classes that will be instantiated in the testbench. It will be registered in the UVM factory by using the uvm_resource_db method and every block that will use the same interface, will need to get it by calling the same method. It might start to look complex, but for now we won’t need to worry about it too much.
  • Running the test.
    The source for the top block is represented in Code 3.2.

`include "simpleadder_pkg.sv"
`include "simpleadder.v"
`include "simpleadder_if.sv"
 
module simpleadder_tb_top;
     import uvm_pkg::*;
 
     //Interface declaration
     simpleadder_if vif();
 
     //Connects the Interface to the DUT
     simpleadder dut(vif.sig_clock,
                     vif.sig_en_i,
                     vif.sig_ina,
                     vif.sig_inb,
                     vif.sig_en_o,
                     vif.sig_out);
     initial begin
          //Registers the Interface in the configuration block
          //so that other blocks can use it
          uvm_resource_db#(virtual simpleadder_if)::set(.scope("ifs"), .name("simpleadder_if"), .val(vif));
 
          //Executes the test
          run_test();
     end
 
     //Variable initialization
     initial begin
          vif.sig_clock = 1'b1;
     end
 
     //Clock generation
     always
          #5 vif.sig_clock = ~vif.sig_clock;
     endmodule

Code 3.2: Top block – simepladder_tb_top.sv

A brief explanation of the code will follow:

  • The lines 2 and 3 include the DUT and the interface into the top block, the line 5 imports the UVM library, lines 11 to 16 connect the interface signals to the DUT.
  • Line 21 registers the interface in the factory database with the name simpleadder_if.
  • Line 24 runs one of the test classes defined at compilation runtime. This name is specified in the Makefile.
  • Line 34 generates the clock with a period of 10 time units. The time unit is also defined in the Makefile.


For more information about interfaces, you can consult the book “SystemVerilog for Verification: A Guide to Learning the TestBench Language Features“, chapter 5.3.

UVM - Defining The Verification Environment

Before understanding UVM, we need to understand verification.

Right now, we have a DUT and we will have to interact with it in order to test its functionality, so we need to stimulate it. To achieve this, we will need a block that generates sequences of bits to be transmitted to the DUT, this block is going to be named sequencer.

Usually sequencers are unaware of the communication bus, they are responsible for generating generic sequences of data and they pass that data to another block that takes care of the communication with the DUT. This block will be the driver.

While the driver maintains activity with the DUT by feeding it data generated from the sequencers, it doesn’t do any validation of the responses to the stimuli. We need another block that listens to the communication between the driver and the DUT and evaluates the responses from the DUT. This block is the monitor.

Monitors sample the inputs and the outputs of the DUT, they try to make a prediction of the expected result and send the prediction and result of the DUT to another block, the scoreboard, in order to be compared and evaluated.

All these blocks constitute a typical system used for verification and it’s the same structure used for UVM testbenches.

You can find a representation of a similar environment in Figure 2.1.

ch3-uvm_tb_typical

Figure 2.1: Typical UVM testbench

Usually, sequencers, drivers and monitors compose an agent. An agent and a scoreboard compose an environment. All these blocks are controlled by a greater block denominated of test. The test block controls all the blocks and sub blocks of the testbench. This means that just by changing a few lines of code, we could add, remove and override blocks in our testbench and build different environments without rewriting the whole test.

To illustrate the advantage of this feature, let’s imagine a situation where we are testing a another DUT that uses SPI for communication. If, by any chance, we want to test a similar DUT but with I2C instead, we would just need to add a monitor and a driver for I2C and override the existing SPI blocks, the sequencer and the scoreboard could reused just fine.

UVM Classes

The previous example demonstrates one of the great advantages of UVM. It’s very easy to replace components without having to modify the entire testbench, but it’s also due to the concept of classes and objects from SystemVerilog.

In UVM, all the mentioned blocks are represented as objects that are derived from the already existent classes.

A class tree of the most important UVM classes can be seen in Figure 2.2.

ch3-uvm_tb_class_tree

Figure 2.2: Partial UVM class tree

The data that travels to and from our DUT will stored in a class derived either from uvm_sequence_item or uvm_sequence. The sequencer will be derived from uvm_sequencer, the driver from uvm_driver, and so on.

Every each of these classes already have some useful methods implemented, so that the designer can only focus on the important part, which is the functional part of the class that will verify the design. These methods are going to addressed further ahead.

For more information about UVM classes, you can consult the document Accellera’s UVM 1.1 Class Reference.

UVM Phases

All these classes have simulation phases. Phases are ordered steps of execution implemented as methods. When we derive a new class, the simulation of our testbench will go through these different steps in order to construct, configure and connect the testbench component hierarchy.

The most important phases are represented in Figure 2.3.

ch3-uvm_tb_phases

Figure 2.3: Partial list of UVM phases

A brief explanation of each phase will follow:

  • The build phase is used to construct components of the hierarchy. For example, the build phase of the agent class will construct the classes for the monitor, for the sequencer and for the driver.
  • The connect is used to connect the different sub components of a class. Using the same example, the connect phase of the agent would connect the driver to the sequencer and it would connect the monitor to an external port.
  • The run phase is the main phase of the execution, this is where the actual code of a simulation will execute.
  • And at last, the report phase is the phase used to display the results of the simulation.

There are many more phases but none of them are mandatory. If we don’t need to have one in a particular class, we can just omit it and UVM will ignore it.

More information about UVM phasing can be consulted in Verification Academy’s UVM Cookbook, page 48.

UVM Macros

Another important aspect of UVM are the macros. These macros implement some useful methods in classes and in variables. they are optional, but recommended.

The most common ones are:

  • `uvm_component_utils – This macro registers the new class type. It’s usually used when deriving new classes like a new agent, driver, monitor and so on.
  • `uvm_field_int – This macro registers a variable in the UVM factory and implements some functions like copy(), compare() and print().
  • `uvm_info – This a very useful macro to print messages from the UVM environment during simulation time.

This guide will not go into much detail about macros, their usage is always the same for every class, so it’s not worth to put much thought into it for now.

More information can be found in Accellera’s UVM 1.1 Class Reference, page 405.

 

SimpleAdder UVM Testbench

After a brief overview of a UVM testbench, it’s time to start developing one. By the end of this guide, we will have the verification environment from the Figure 2.4.

ch3-uvm_tb_simpleadder_complete

Figure 2.4: SimpleAdder Final Testbench

This guide will begin to approach the top block and the interface (chapter 3), then it will explain what data will be generated with the sequences and sequencers on chapter 4.

Following the sequencers, it will explain how to drive the signals into the DUT and how to observe the response in chapters 5 and 6 respectively.

Subsequently, it will explain how to connect the sequencer to the driver and the monitor to the scoreboard in chapter 7. Then it will show to build a simple scoreboard in chapter 8.

And finally, the test will be executed and analyzed.

The testbench can be run with the execution of a Makefile provided in the repository. As I mentioned previously, this Makefile uses Synopsys VCS but it should be easily modifiable to be executed with any HDL simulator.

UVM Tutorial - The DUT

This training guide will focus on showing how we can build a basic UVM environment, so the device under test was kept very simple in order to emphasize the explanation of UVM itself.

The DUT used is a simple ALU, limited to a single operation: the add operation. The inputs and outputs are represented in Figure 1.1.

ch2-dut

Figure 1.1: Representation of the DUT’s inputs/outputs

This DUT takes two values of 2 bits each, ina and inb, sums them and sends the result to the output out. The inputs are sampled to the signal of en_i and the output is sent at the same time en_o is signalled.

The operation of the DUT is represented as a timing diagram and as a state machine in Figure 1.2.

ch2-dut_timing_diagram

ch2-dut_state_machine

Figure 1.2: Operation of the DUT

Below is the code for sample DUT

UVM Introduction

As digital systems grow in complexity, verification methodologies get progressively more essential. While in the early beginnings, digital designs were verified by looking at waveforms and performing manual checks, the complexity we have today don’t allow for that kind of verification anymore and, as a result, designers have been trying to find the best way to automate this process.

The SystemVerilog language came to aid many verification engineers. The language featured some mechanisms, like classes, covergroups and constraints, that eased some aspects of verifying a digital design and then, verification methodologies started to appear.

UVM is one of the methodologies that were created from the need to automate verification. The Universal Verification Methodology is a collection of API and proven verification guidelines written for SystemVerilog that help an engineer to create an efficient verification environment. It’s an open-source standard maintained by Accellera and can be freely acquired in their website.

By mandating a universal convention in verification techniques, engineers started to develop generic verification components that were portable from one project to another, this promoted the cooperation and the sharing of techniques among the user base. It also encouraged the development of verification components generic enough to be easily extended and improved without modifying the original code.

All these aspects contributed for a reduced effort in developing new verification environments, as designers can just reuse testbenches from previous projects and easily modify the components to their needs.

These series of webpages will provide a training guide for verifying a basic adder block using UVM. The guide will assume that you have some basic knowledge of SystemVerilog and will require accompaniment of the following resources:

This guide will be divided in 3 different parts:

  • The first part, starting on chapter 1, will explain the operation of the device under test (DUT): the inputs, the outputs and the communication bus
  • The second part, starting on chapter 2, will give a brief overview of a generic verification environment and the approach into verifying the DUT
  • The third part, starting on chapter 3, will start to describe a possible UVM testbench to be used with our DUT with code examples. It’s important to consult to the external material in order to better understand the mechanism behind the testbench.

Transient Materials - Electronics that melt away

transient_materialsImagine tossing your old phone in the toilet, watching it dissolve and then flushing it down, instead of having it wind up in a landfill. Scientists are working on electronic devices that can be triggered to disappear when they are no longer needed.

The technology is years away, but Assistant Professor Reza Montazami and his research team in the mechanical engineering labs at Iowa State University have published a report that shows progress is being made. In the two years they've been working on the project, they have created a fully dissolvable and working antenna.

"You can actually send a signal to your passport via satellite that causes the passport to physically degrade, so no one can use it," Montazami said.

The electronics, made with special "transient materials," could have far-ranging possibilities. Dissolvable electronics could be used in medicine for localizing treatment and delivering vaccines inside the body. They also could eliminate extra surgeries to remove temporarily implanted devices.The military could design information-gathering gadgets that could complete their mission and dissolve without leaving a trace.

The researchers have developed and tested transient resistors and capacitors. They’re working on transient LED and transistor technology, said Montazami, who started the research as a way to connect his background in solid-state physics and materials science with applied work in mechanical engineering.

As the technology develops, Montazami sees more and more potential for the commercial application of transient materials.

EDA Playground–An Awesome Online Tool

eda-playground-01Many times we use the web to find code examples and tutorials. However, often the examples were incomplete. Sometimes they were missing the necessary code to hook the example into a real design. Other times, the code examples had syntax errors.

Sometime we are presented with a working design, with lines stripped out, but with undefined variables and dangling commas left in. Other times the code examples simply did not work on my simulator. All this resulted in endless frustration to us. I knew there had to be a better way, EDA Playground is one.

EDA Playground is a free web application that allows users to edit, simulate (and view waveforms), synthesize, and share their HDL code. Its goal is to accelerate the learning of design and testbench development with easier code sharing and with simpler access to simulators and libraries. EDA Playground is specifically designed for small prototypes and examples (it is not intended to be used for a full-blown FPGA or ASIC design).

EDA Playground gives engineers immediate hands-on exposure to simulating SystemVerilog, Verilog, VHDL, C++/SystemC, and other HDLs. All you need is a web browser. The goal is to accelerate learning of design/testbench development with easier code sharing, and with simpler access to EDA tools and libraries. EDA Playground is specifically designed for small prototypes and examples.

  • With a simple click, run your code and see console output in real time. Pick another simulator version and run it again.
  • View waves for your simulation using EPWave browser-based wave viewer.
  • Save your code snippets. Share your code and simulation results with a web link. Perfect for web forum discussions or emails. Great for asking questions or sharing your knowledge.
  • Quickly try something out
    • Try out a SystemVerilog feature before using it on your project.
    • Try out a library that you’re thinking of using.
    • Modify another engineer’s shared code and re-run it.
  • Eliminate environment differences. Since the code always executes in the same environment, everyone will see the same result on a subsequent re-run.
  • Browse and use a large repository of working code examples and templates.

 

Google To Develop Quantum Processors

quantumx299Google was one of the early backers of a new approach to quantum computing adopted by a company called D-Wave. The company offers boxes that perform a process called quantum annealing instead of the more typical approach, which involves encoding information in a quantum state of a collection of entangled qubits. Although whatever D-Wave is doing is clearly quantum, it's still not clear that it offers a speedup compared to classical computers.

So rather than keeping all its eggs in D-Wave's basket, Google's "Quantum A.I. Lab" announced that it is starting a collaboration with an academic quantum computing researcher, John Martinis of the University of California-Santa Barbara. Martinis' group focuses on creating fault-tolerant qubits using a solid-state superconducting structure called a Josephson junction. By linking several of these junctions and spreading a single quantum state across them, it's possible to reach fidelities of over 99 percent when it comes to storing the quantum state.

Quantum states tend to be fragile and decay when they interact with their environment, so a lot of labs are working on making qubits that are more robust or have error correcting ability. Josephson junctions are one possible approach to this, but they have the advantage of being on familiar turf for computing companies, since they can be made by standard fabrication techniques (although they still need to be chilled to near absolute zero).

Google made it clear that it's not turning its back on D-Wave; the new work will be done in parallel. As for quantum computing itself, this is interesting news. A quick look at our past coverage makes it clear that there are a number of technologies that appear to be getting close to the point where they could be used to create a multi-qubit machine. Google's decision to push hard on one of these approaches could narrow the field—either by getting Josephson junctions to work or by showing that there are severe limitations to them.

Do You Know Internet Of Things !!!

The Internet of Things (IoT, also Cloud of Things or CoT) refers to the interconnection of uniquely identifiable embedded computing like devices within the existing Internet infrastructure. The Internet of Things is a scenario in which objects, animals or people are provided with unique identifiers and the ability to transfer data over a network without requiring human-to-human or human-to-computer interaction. IoT has evolved from the convergence of wireless technologies, micro-electromechanical systems (MEMS) and the Internet. The creativity of this new era is boundless, with amazing potential to improve our lives.

Here are the six main attributes that make "things" a part of the Internet Of Things, or IoT:

  • Sensors: IoT devices and systems include sensors that track and measure activity in the world. One example is Smartthings' open-and-close sensors that detect whether or not a drawer, window, or door in your home is open or closed.
  • Connectivity: Internet connectivity is either contained in the item itself, or a connected hub, smartphone, or base station. If it's the latter, then the base station will likely be collecting data from an array of sensor-laden objects, and relaying data to the cloud and back.
  • Processors: Just like any computing device, IoT devices will contain some computing power "under the hood," if only to be able to parse incoming data and transmit it.
    These characteristics all apply to today's smartphones, of course, but many IoT devices will also need to be equipped with several special features to be truly useful. These will differentiate IoT devices, particularly remote ones, from today's smartphones.
  • Energy-efficiency: Many devices in the IoT may be difficult, costly, or dangerous to access for charging or battery replacement. One may even think of the Mars Curiosity Rover as an example of such a device. Therefore, they may need to be able to operate for a year or more unattended using a conservative amount of energy or be able to wake up only periodically to relay data.
  • Cost-effectiveness: Objects that contain sensors may need to be distributed broadly to be useful, as in the case of sensors in food products in supermarkets that would indicate if an item has spoiled. These would need to be relatively inexpensive to purchase and deploy.
  • Quality and reliability: Some IoT devices will need to operate in harsh environments outdoors and for extended periods of time.
  • Security: IoT devices may need to relay sensitive or regulated information such as health-related data, so data security will be critical.

Internet Of Things explained by Arlen Nipper.
Arlen Nipper has been designing embedded computer hardware and software for 33 years. Arlen graduated from Oklahoma State University and worked in the oil patch for 10 years learning tons of useful stuff about "how things work" in the real world.

Get free daily email updates!

Follow us!

VHDL Coding Tips For Excellent Design

Here are the Ten Commandments of Excellent Design:
  1. All state machine outputs shall always be registered
  2. Thou shalt use registers, never latches
  3. Thy state machine inputs, including resets, shall be synchronous
  4. Beware fast paths lest they bite thine ankles
  5. Minimize skew of thine clocks
  6. Cross clock domains with the greatest of caution. Synchronize thy sig- nals!
  7. Have no dead states in thy state machines
  8. Have no logic with unbroken asynchronous feedback lest the fleas of myriad Test Engineers infest thee
  9. All decode logic must be crafted carefully—eschew asynchronicity
  10. Trust not thy simulator—it may beguile thee when thy design is junk
How to Write Ten-Commandment Code

Conforming to the Ten Commandments is not difficult. In this section you’ll see how to write VHDL (your author doesn’t do Verilog, but the translation is easy) that complies with the rules. Robust design and first-silicon success are the goals!

The philosophy behind Ten-Commandment code is that synthesizers are not to be trusted too much. Most of the code you will see is close to the structural level; some more overtly than others. Most of the code is self-explanatory. It is assumed that the reader is familiar with VHDL. Signal names are also obvious to anyone “skilled in the art.”

How to Create a Flip-Flop
One of the basic primitives that we need to create robust synchronous designs is the D-type flip-flop. Look at the code in Code Sample 1: A D-type Flip Flop

-- VHDL Code for a D-Type Flip-Flop with an -- Asynchronous Clear
D_Type_Flip_Flop: process(Reset_n, Clock_In)
  begin
    if (Reset_n = ’0’) then
      Q_Output <= ’0’ after 1 ns;
  elsif (Clock_In’event and Clock_In = ’1’) then
      Q_Output <= D_Input after 1 ns;
  end if;
end process D_Type_Flip_Flop;

This flip-flop has the following properties:
  • An asynchronous active-low clear input sets the Q output to zero.
  • It is triggered on the rising edge of the clock.

How to Create a Latch
While the Ten Commandments specifically forbid the use of latches, there are still those heretics who will insist on the use of latches. The code to instantiate a transparent latch is shown in Code Sample 2: A Transparent Latch

-- VHDL Code for a Transparent Latch
Latch_Data: process(Latch_Open, D_Input)
  begin
    if (Latch_Open = ’1’) then
      Latched_Data <= D_Input;
    -- If Latch_Open = 0, then Latched_Data keeps its old value, -- i.e. the latch is closed.
    end if;
end process Latch_Data;

This latch has the following properties:
  • A latch control that opens the latch when high (the latch is transparent).
How to Create a Metastable- Hardened Flip-Flop

The use of a metastable-hardened flip flop is nothing more than the direct instantiation of a suitable library element in this case, a “dfntns” flip-flop. This is pure structural VHDL. The component declaration is shown in Code Sample 3:  A Metastable-Hardened Flip-Flop, Component Declaration

-- VHDL Code for a Nice Metastable-Hardened Flip-Flop
component dfntns
  Port (
    CP : In std_logic;
    D : In std_logic;
    Q : Out std_logic
  );
end component;

To use the flip-flop in your circuit, instantiate it as shown in Code Sample 4: A Metastable-Hardened Flip-Flop, Instantiation

-- VHDL Code to Instantiate the Metastable-Hardened Flip-Flop
Metastable_Hardened_Flip_Flop_Please: dfntns port map (
  D => D_Input,
  CP => Clock_In,
  Q => Q_Output
  );

This flip-flop has the following properties:
  • A maximum clock-to-out time under worst-case setup and hold time violations. This time is available in the library element specifications.
The Care and Feeding of Toggle Signals

Receiving a Toggle Signal
The Ten Commandments paper suggested that a method for exchanging single- point information across clock domains is by the use of toggle signals. Here, it is assumed that the toggle event should generate an active-high pulse to pass to a state machine. Every toggle—rising edge and falling edge—must create the pulse. In addition, the pulse must be synchronized correctly to the receiver’s clock. The code to accomplish this is shown in Code Sample 5: Receiving a Toggle Signal

-- VHDL Code to Create a Pulse from an Asynchronous -- Toggle Signal
-- First, use a metastable-hardened flip-flop to synchronize the -- toggle input
Metastable_Hardened_Flip_Flop_Please: dfntns port map (
  D => Handshake_T,
  CP => Clock_In,
  Q => Sync_Handshake_T
  );

-- Now pass the synchronized toggle through another flip-flop
Toggle_Reg_Proc: process(Clock_In)
  begin
    if (Clock_In'event and Clock_In = ’1’) then
      Reg_Handshake_T <= Sync_Handshake_T after 1 ns;
    end if;
  end process Toggle_Reg_Proc;

-- Finally XOR the two synchronized signals to create a pulse
Toggle_Pulse <= Reg_Handshake_T xor Sync_Handshake_T;

When synthesizing this code, remember to use the “fix hold” option so a fast path doesn't occur between the two flip-flops in this circuit.

Generating a Toggle Signal
Recall that a toggle signal is generated by simply inverting a level to pass the information. The trivial code to do this is shown in Code Sample 6:  Generating a Toggle Signal

The suffix “_T” is used to denote a toggle signal.

-- VHDL Code to Create a Toggle Signal
Handshake_T <= not (Handshake_T) after 1 ns;

Coding State Machines
The creation of state machines is a mixture of art and science. A well-crafted state machine will possess a sense of elegance; it will be appealing, both functionally and visually.
Here, a very simple example is presented as an illustration of state machine design. The state diagram for the Flintstones State Machine is shown in figure below popularly known as The Flintstones State Machine.


The Flintstones State Machine operates as follows:
  1. The State Machine has two states, State Bed and State Rock.
  2. There is one output, Fred, which takes the value 0 in State Bed and 1 in State Rock.
  3. A reset, caused by a low level on Reset_n, puts the State Machine into State Bed.
  4. The State Machine waits in State Bed while Barney is low, and enters State Rock when Barney goes high.
  5. The State Machine then waits in State Rock while Wilma is low, and returns to State Bed when Wilma goes high.
Implementing the Flintstones State Machine

An example implementation of the Flintstones State Machine is shown in Code Sample 7 below:

-- VHDL Code to Implement the Flintstones State Machine

Flintstones_SM_Proc: process(Sync_Reset_n, Clock_In)
  -- Enumerate state types:
  type Flintstones_Statetype is (
  Bed, Rock
  );

  -- define the state variable:
  variable Flint_State: Flintstones_Statetype;

  -- Here’s the state machine:
  begin
  -- Define the asynchronously set reset states...
    if (Sync_Reset_n = ’0’) then
      Fred <= ’0’ after 1 ns;
      Flint_State := Bed
      -- Default conditions for each output, in this case identical to the -- reset state:
    elsif (Clock_In’event and Clock_In = ’1’) then
      Fred <= ’0’ after 1 ns;

    -- Here are the state transitions:
    case Flint_State is
      when Bed =>
      -- Transition from Bed to Rock:
        if (Barney = ’1’) then
          Fred <= ’1’ after 1 ns;
          Flint_State := Rock;
          -- Holding term in Bed:
        else
          Flint_State := Bed;
        end if;
      when Rock =>
        -- Transition from Rock to Bed:
        if (Wilma = ’1’) then
          Fred <= ’0’ after 1 ns;
          Flint_State := Bed;
        -- Holding term in Rock:
        else
          Fred <= ’1’ after 1 ns;
          Flint_State := Rock;
        end if;
      -- Default term for dead states:
      when others =>
        Flint_State := Bed;
    end case;
  end if;
end process Flintstones_SM_Proc;


Notes on the State machine Implementation
For the most part, the Flintstones State Machine’s operation should be clear. A few points are worth noting, however:
  1. The reset signal (Sync_Reset_n) is synchronized with Clock_In before being sent to the State Machine.
  2. Barney and Wilma must also be synchronous to Clock_In; at the very least, there must be an assurance that the State Machine’s state and output regis- ter’s setup and hold times are not violated.
  3. This design assigns a default value to each output and to the state variable before entering the case statement. This ensures that only those signals that are not taking default (usually inactive) values need be listed in the case statement. This is optional; it is entirely reasonable to list every signal under each transition term, including inactive signals.
  4. Note that the output signal Fred comes directly from a D-type flip-flop: it is not a decode of the state variable. This ensures Fred’s cleanliness (so to speak).
  5. The “when others” in the case statement handles the possibility that the State Machine might end up in a dead state.
The code examples in this tutorial should be considered as examples only. There are many ways to code excellent VHDL; this code is a place to start. If you have a neat snippet of VHDL to add to the list, then please post your valuable comments below.

Calculations using command-line

calculatorMany of you do most of your work from the command-line, using vim to edit files, mutt for e-mails, cd/ls/mv/find/etc instead of a file manager, then you may get annoyed by having to fire up a GUI calculator to make (what may sometimes be) a single calculation.

One useful feature of calculating on the command-line is that you can see what you've typed. For instance, sometimes when I'm entering a long, complex calculation on a calculator (either the GUI or the solid, hold-in-your-hand type), I sometimes forget if I've actually typed in all those numbers or made the calculations in the right order. Maybe it's just me ...

This article shows how to quickly perform standard calculations on the command line including addition, subtraction, multiplication, division, square root, powers, conversion from decimal to hex, decimal to binary, hex to decimal, and binary to decimal. It also briefly introduces using bc in interactive mode and how to write files for use with bc for frequently repeated operations. There is a mention of using Google for performing calculations. It finishes with a little challenge to test the power of your CPU.

Other advantages of using bc include:

  • bc is included with (almost?) all Linux distributions as standard, as well as (again, almost?) all Unix.
  • Results from calculations in some proprietary flavours of bc have up to 99 decimal digits before and after the decimal point. This limit has been greatly surpassed in GNU bc. I don't know what that limit is, but it's at least many, many tens of thousands. Certainly it's more than any GUI-based calculators (I've used) could accommodate.
  • You may also find yourself working in an environment where you simply don't have access to a GUI.
  • The syntax for basic sums is almost identical to Google's calculator function, so you can learn how to use two utilities in one go!

bc is a pre-processor for dc. The useful thing about bc is that it accepts input from files and from standard input. This allows us to pipe data to it for quick calculations.

  • addition
  • subtraction
  • multiplication
  • scale
  • division
  • square root
  • power
  • parentheses
  • obase and ibase
  • convert from decimal to hexadecimal
  • convert from decimal to binary
  • convert from binary to decimal
  • convert from hexadecimal to decimal
  • a brief introduction to interactive mode
  • using bc with shell scripts
  • a brief introduction to using bc with files
  • a quick challenge for your PC (GNU bc only)

Most of these examples follow a simple formula.

addition

$ echo '57+43' | bc
100

subtraction

$ echo '57-43' | bc
14

multiplication

$ echo '57*43' | bc
2451

scale
The scale variable determines the number of digits which follow the decimal point in your result. By default, the value of the scale variable is zero. (Unless you use the l option in which case it defaults to 20 decimal places. More about l later.) This can be set by declaring scale before your calculation, as in the following division example:

division

$ echo 'scale=25;57/43' | bc
1.3255813953488372093023255

square root

$ echo 'scle=30;sqrt(2)' | bc
1.414213562373095048801688724209
This beats Google's calculator function which only calculates the result to 8 decimal places! ;-) Although Google's calculator function has this 8 decimal places limitation, it will allow imaginary numbers as answers.

power

$ echo '6^6' | bc
46656

parentheses
If you have read Robert Heinlein's The Number of the Beast, you may recall that the number of parallel universes in the story equals (six to the power of six) to the power of six. If you should try to calculate that like this:

$ echo '6^6^6' | bc
You will get a screen full of numbers (some 37374 digits), not the
10314424798490535546171949056

that you might expect.

If you're running a non-GNU version of bc, you'll most likely get something like:

exp too big
empty stack
save:args

The Google Calculator balks at '6^6^6' as well. Good ol' GNU.

That's because you typed the wrong question. You need to type:

$ echo '(6^6)^6' | bc

Whereas what you did type was interpreted as:

$ echo '6^(6^6)' | bc

which is an entirely different number altogether. So the positioning of parentheses (brackets to you and me!) is very important. I use brackets to separate the different components of my sums whenever possible, just eliminate any possible doubt that I may get the wrong answer. Consider the following calculations:

$ echo '7+(6*5)' | bc

$ echo '7+6*5' | bc

$ echo '6*5+7' | bc

They all give the same answer, 37, but I would have typed the first calculation, unless of course, I meant:

$ echo '(7+6)*5' | bc

Or to put it another way:

$ echo '13*5' | bc

which is 65.

obase and ibase

obase and ibase are special variables which define output and input base.

Legitimate obase values range from 2 to 999, although anything beyond 16 is wasted on me!

Legitimate ibase values range from 2 to 16.

Some examples will explain all this better.

convert from decimal to hexadecimal

Here we're converting 255 from base 10 to base 16:

$ echo 'obase=16;255' | bc
FF

convert from decimal to binary

And here we're converting the number 12 from base 10 to base 2:

$ echo 'obase=2;12' | bc
1100

Which reminds me of the old joke:

There are only 10 types of people in the world -- those who understand binary, and those who don't.

Which leads us neatly onto the next example:

convert from binary to decimal

Here we're converting the binary number 10 to a base 10 (decimal) number.

$ echo 'ibase=2;obase=A;10' | bc
2

Note that the obase is "A" and not "10". Sorry, you've got to learn some hex. The reason for this is you've set the ibase to "2", so if you now had tried to use "10" as the value for the obase, it would stay as "2", because "10" in base 2 is "2". So you need to use hex to "break out" of binary mode.

Well, that was just to explain the joke; now something a bit more challenging:

$ echo 'ibase=2;obase=A;10000001' | bc
129

convert from hexadecimal to decimal

$ echo 'ibase=16;obase=A;FF' | bc
255

Again, note the use of "A" to denote base 10. That is because "10" in hex (base 16 - the ibase value) is 16.

a brief introduction to interactive mode
You can also run bc in interactive mode:

$ bc

If you're running GNU bc, you should get the following notice:

bc 1.06
Copyright 1991-1994, 1997, 1998, 2000 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.
Followed by an uninviting blank prompt. Otherwise you'll just get an uninviting blank prompt.
If you wish to get straight to the uninviting blank prompt, use the -q option, which runs bc in quiet mode, preventing the normal GNU bc welcome from being printed:
$ bc -q

Using the basics we've been through from the examples above, enter a calculation:

scale=5
57/43
1.32558


Type quit to exit bc interactive mode.


using bc with shell scripts
You can use shell variables with bc, which is very useful in shell scripts:


$ FIVE=5 ; echo "$FIVE^2" | bc
25


Note the use of double-quotes to preserve the value of the variable $FIVE.

a brief introduction to using bc with files

Using bc with files allows complex calculations to be repeated, again and again, a bit like using a spreadsheet to run the same calculations on changing figures ... but faster.


Here is a simple example:

scale=2

/* C-style comments
are allowed, as are spaces */

print "\nConvert Fahrenheit to Celsius\n\n"
print "Temperature in Fahrenheit: " ; fah = read()
print "\n"
print "Equivalent Temperature in Celsius is: "
(fah - 32.0) * 5.0 / 9.0
quit

Create and save the file, then run it like this:

$ bc -q filename
Convert Fahrenheit to Celsius
Temperature in Fahrenheit: 61
Equivalent Temperature in Celsius is: 16.11


Note that this example has only been tested with GNU bc. Other (proprietary) versions of bc may have more stringent syntax requirements. Some bcs don't allow the use of print or read, for example, so you have to edit your file before each calculation. Not very useful.


a quick challenge for your PC (GNU bc only)
If you wish to test the comparative speed of your PC, try this challenge: use bc to calculate Pi to 5000 decimal places. The idea for this challenge came from a great article at Geekronomicon.
If you really want to tie up your machine for an hour (or more), you could try the "Pi to 25000 decimal places" challenge from the aforementioned Geekronomicon.

First, to put things in perspective, here is some information about my CPU:

$ cat /proc/cpuinfo | egrep "model name|MHz"
model name      : AMD Athlon(tm) 64 Processor 3500+
cpu MHz         : 2211.346


Note the use (below) of the command bc –l -q.
-l loads the math library which is required for the use of the "2" (arctangent) in the calculation for Pi. You can learn more about the math library functions in the
bc command manual.
I'm not sure what effect the -q option (quiet, no welcome message printed) has on our test, but I guess it can't harm.

$ time echo "scale=5000; 4*a(1)" | bc -l -q
3.141592653589793238462643383279502884197169399375105820974944592307\
...
...
...
73774418426312986080998886874132604720


real    0m44.164s
user 0m44.099s
sys 0m0.008s
44.099 seconds! Not bad. :-) I imagine that some Gentoo folks may be interested to see what difference their compile-time optimisations make to the speed of bc. FWIW, my distro of choice is Arch Linux.

Popular Posts