Skip to content

This repository is designed for anyone looking to get started with cocotb. The inspiration for creating this came from my experience in the Dyumin Semiconductor Mentorship Program. I’m especially grateful to Vijayvithal Jahagirdar for his insightful playlist on cocotb, which serves as an excellent introduction for beginners.

Notifications You must be signed in to change notification settings

VarunGaneshan/Learn_Cocotb

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

74 Commits
 
 
 
 
 
 
 
 

Repository files navigation

My Learning Journey

image

  • cocotb is a coroutine-based co-simulation library for writing HDL test benches in Python. All you need is Python, gnu make and a hdl simulator.

  • A coroutine is a special type of function that can pause its execution (using await) and resume later. It allows for asynchronous programming, meaning the program can perform non-blocking waits and handle other tasks in the meantime, making it more efficient.

  • In Cocotb, coroutines are used to simulate hardware behavior over time. Hardware tests typically involve waiting for certain signals or clocks, and coroutines allow the test to wait for these events in an efficient way without blocking the entire program.

Line 2 and 3 will get clarified here-Or Verification Demystifed

Environment Set Up

Installing prerequisites & iverilog:

Installation

Screenshot from 2024-09-05 17-14-05

sudo apt-get install make python3 python3-pip libpython3-dev
sudo apt-get install iverilog

Creating Virtual Environment:

Possible Error & Fix:

Screenshot from 2024-09-05 18-59-17

python3 -m venv venv

This makes sure that the global Python installation isn't corrupted.

image

sudo apt-get install python3-venv

image

which python3
source venv/bin/activate

Installing necessary packages:

Possible Error & Fix: Install missing wheel package image image

pip3 install pytest cocotb cocotb-bus cocotb-coverage

image

pip3 install wheel

image

ls venv/lib/python3.6/site-packages/

packages sucessfully installed

Or Verification Example

Using Local Simulation:

git clone https://github.com/learn-cocotb/tutorial.git
cd tests/

image

make or

Makefile Structure: image

vi Makefile

SIM ?= icarus #simulator used,?= means "set the variable only if it is not defined."
TOPLEVEL_LANG ?= verilog
#declaring source files that will be compiled and simulated.
VERILOG_SOURCES += $(PWD)/../hdl/or_gate.v 
VERILOG_SOURCES += $(PWD)/wrappers/or_test.v
#define make target
or:
	rm -rf sim_build #clean build
	$(MAKE) sim MODULE=or_test TOPLEVEL=or_test #python test file and top-level Verilog module
include $(shell cocotb-config --makefiles)/Makefile.sim #This line includes the default Cocotb Makefile,It is always declared at last as it has its make target which can run instead of 'or' as default 'make' takes the first target.
  • $(shell cocotb-config --makefiles): This calls the cocotb-config command, which knows where the Cocotb installation is located, and fetches the Makefile.sim file.

  • Makefile.sim: This is a core Cocotb file that contains all the common rules for running the simulation, compiling, and linking your test with Cocotb.

To exit vim editor - : -> q! enter , for more type vimtutor on terminal

Using Github actions:

Go to the actions tab and create a new workflow.

image

image

name: learning-cocotb
run-name: ${{ github.actor }} is learning Cocotb
on:
  workflow_dispatch:

jobs:
  verify:
    runs-on: ubuntu-latest
    timeout-minutes: 3
    permissions:
      contents: write
      checks: write
      actions: write
    steps:
      - uses: actions/checkout@v3
      
      - run: sudo apt install -y --no-install-recommends iverilog
      
      - run: pip3 install cocotb cocotb-bus
      
      - run: make -C tests or
      
      - uses: actions/upload-artifact@v4
        with:
          name: waveform
          path: tests/*.vcd

      - name: Publish Test Report
        uses: mikepenz/action-junit-report@v3
        if: always()
        with:
          report_paths: '**/tests/results.xml'
on: [push] - run every time you push
To run it manually use
on:
	workflow_dispatch:

image

image

image

Waveform:

Download the artifact from Summary.

image

Online waveform viewer

image

Gtkwave

image

gtkwave waveform.vcd

Clone the repo locally: image

git clone https://github.com/learn-cocotb/assignment-xor.git
cd assignment-xor/
cd tests

image

vi dut_test.py
  • assert 0: Always fails, triggering an assertion failure. It can be used to deliberately fail a test.

  • assert 1: Always passes, so it doesn't trigger any assertion failure.

image

make
Or Verification Demystifed

Truth table-Expected Result/Functional Behaviour:

image

  • OR gate design here would be the DUT, and you'd write Python code to drive the OR gate inputs and check the outputs against expected results.

  • DUT here stands for Device Under Test. It refers to the specific piece of hardware that is being tested or simulated.

gedit hdl/or_gate.v
module or_gate(
	input wire a,
	input wire b,
	output wire y
);
assign y=a|b; //DUT
endmodule
gedit tests/or_test.py
import cocotb 
from cocotb.triggers import Timer, RisingEdge 

@cocotb.test() #this decorator pulls test-related features
async def or_test(dut): 
    a = (0, 0, 1, 1) #tuples are immutable-here values are predefined and won't change during the test
    b = (0, 1, 0, 1)
    y = (0, 1, 1, 1)

    for i in range(4): #0 to 3
        dut.a.value = a[i] 
        dut.b.value = b[i]
        await Timer(1, 'ns') 
        assert dut.y.value == y[i], f"Error at iteration {i}" 
  • The Timer trigger pauses the execution for a specified amount of time, simulating delays or waiting for a certain duration within your test.

  • RisingEdge waits for a signal to transition from low to high (0 to 1). It’s useful when synchronizing your test with clock edges or specific events in the DUT. We are not using it here.Similarly FallingEdge and Edge can also be used.

  • A decorator is defined as a function that takes another function as an argument and returns a new function that usually extends or alters the behavior of the original function.

def check(func):
    def inside(a, b):
        if b == 0:
            print("Can't divide by 0")
            return
        func(a,b)
    return inside

@check 
def div(a, b):
    return a / b
    
#div=check(div)    
print(div(10, 0))
  • @cocotb.test(): This is a decorator that marks the function or_test as a Cocotb test. Cocotb will automatically recognize this function and execute it as part of the test suite.

  • async def or_test(dut): This defines the test function as an asynchronous coroutine (async), which allows you to use await inside the function for non-blocking waits.

  • dut.a.value = a[i]: This sets the value of the a input of the DUT to the corresponding value in the a tuple for each iteration of the loop.In python even dut.a <= a[i] works.

  • Delta delay is a concept in digital simulation that represents an infinitesimally small unit of time. It is a way to model events that happen at the same simulation time but need to be evaluated in a specific order. In hardware simulations, even though two events might appear to happen "simultaneously" in real-time, the simulator needs to process them in some order. The simulator introduces delta cycles to sequence events that happen at the same time step.

  • await Timer(1, 'ns'): This pauses the execution of the test for 1 nanosecond. It allows time for the DUT to process the input changes and update the output. The await keyword ensures the test waits for the specified time in a non-blocking way.Similarly await Edge(dut.clk) can also be used.

  • Non-blocking operations allow the testbench to simulate these events efficiently, where multiple parts of the hardware can be tested or driven independently without being blocked by one another.

async def test1(dut):
    await Timer(10, 'ns')
    print("Test 1 done")

async def test2(dut):
    await Timer(5, 'ns')
    print("Test 2 done")
  • Both test1 and test2 will start running, and while test1 is waiting for 10 ns, test2 can complete after 5 ns without waiting for test1 to finish. This is non-blocking behavior, as the simulation doesn’t get held up by one test waiting.

  • assert dut.y.value == y[i]: This checks if the actual output of the DUT (dut.y.value) matches the expected output from the y tuple for the current iteration. If the assertion fails, the message is displayed, showing which iteration of the test failed.

cd tests/
gedit Makefile
SIM ?= icarus 
TOPLEVEL_LANG ?= verilog
VERILOG_SOURCES += $(PWD)/../hdl/or_gate.v 
or:
	rm -rf sim_build
	$(MAKE) sim MODULE=or_test TOPLEVEL=or_test 

include $(shell cocotb-config --makefiles)/Makefile.sim 

image

Check for assertion failure:

gedit ../hdl/or_gate.v
module or_gate(
	input wire a,
	input wire b,
	output wire y
);
assign y=a^b; //Xor - must show an error for 1 1 - 0 3rd iteration
endmodule

image

Create a wrapper for RTL code:

  • A wrapper is used to add functionality or integrate RTL code without modifying it directly
  • Change xor to or in hdl/or_gate.v
gedit wrappers/or_test.v
module or_test(
	input wire a,
	input wire b,
	output wire y
);
or_gate or_gate( 
	.a(a),
	.b(b),
	.y(y)
); //call the instance
initial begin
	$dumpfile("orwaves.vcd"); // specifies the name of the VCD (Value Change Dump) file where simulation waveforms will be saved.
	$dumpvars; // dumping all variables to the VCD file.
end
endmodule

Other variants of dumpvars:

  • $dumpvars: Dumps all variables in the current scope.
  • $dumpvars(level): Dumps variables with the specified level of hierarchy. For example, $dumpvars(0) dumps all variables at the top level, while $dumpvars(1) includes variables from one level below, and so on.
  • $dumpvars(level, instance): Dumps variables from a specific instance up to the specified level. For example, - $dumpvars(1, or_gate) would dump variables within the or_gate module and one level below.
  • $dumpvars(level, instance, var): Dumps specific variables from a particular instance and level. This allows for fine-grained control over which signals are dumped.
gedit Makefile

SIM ?= icarus 
TOPLEVEL_LANG ?= verilog
VERILOG_SOURCES += $(PWD)/../hdl/or_gate.v
VERILOG_SOURCES += $(PWD)/wrappers/or_test.v 
or:
	rm -rf sim_build
	$(MAKE) sim MODULE=or_test TOPLEVEL=or_test #modified top level

include $(shell cocotb-config --makefiles)/Makefile.sim 

image

Waveform:

image

image

gtkwave orwaves.vcd
Xor Verification
module xor_gate(
	input wire a,
	input wire b,
	output wire y
);

assign y=a^b;

endmodule
module xor_test(
	input wire a,
	input wire b,
	output wire y
);

xor_gate xg(.a(a),.b(b),.y(y));

initial begin
	$dumpfile("xorwaves.vcd");
	$dumpvars;
end
endmodule
import cocotb
from cocotb.triggers import Timer,RisingEdge

@cocotb.test()
async def xor_test(dut):
	a = (0,0,1,1)
	b = (0,1,0,1)
	y = (0,1,1,0)

	for i in range(4):
		dut.a.value = a[i]
		dut.b.value = b[i]
		await Timer(1,'ns')
		assert dut.y.value==y[i],f"Error at iteration {i}"
SIM ?= icarus 
TOPLEVEL_LANG ?= verilog
TARGET ?= xor 
VERILOG_SOURCES += $(PWD)/../hdl/or_gate.v
VERILOG_SOURCES += $(PWD)/wrappers/or_test.v 
VERILOG_SOURCES += $(PWD)/../hdl/xor_gate.v 
VERILOG_SOURCES += $(PWD)/wrappers/xor_test.v 
or:
	rm -rf sim_build
	$(MAKE) sim MODULE=or_test TOPLEVEL=or_test #modified top level
xor:	
	rm -rf sim_build
	$(MAKE) sim MODULE=xor_test TOPLEVEL=xor_test 
 
run: $(TARGET)

include $(shell cocotb-config --makefiles)/Makefile.sim 

image

make run

Waveform:

image

References

Vijayvithal's Playlist

Docs.cocotb

Learn cocotb

Rahul Behl's How to Get started with Cocotb

Python Decorators

Cocotb for CERN

Git with Vscode

About

This repository is designed for anyone looking to get started with cocotb. The inspiration for creating this came from my experience in the Dyumin Semiconductor Mentorship Program. I’m especially grateful to Vijayvithal Jahagirdar for his insightful playlist on cocotb, which serves as an excellent introduction for beginners.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published