Chapter 2 - Numbers¶
Goal
- SystemVerilog has a rich set of functions and operators for numbers. We will explore Python equivalents of these.
- Let's get straight to business and skip the obvious stuff. Add, subtract, modulo and other operators not mentioned here work the same as SystemVerilog.
Working with hex numbers¶
Examples below show how to work with hex numbers and convert between hex
and int
.
# Just like in C, hex numbers are represented with the prefix `0x`
>>> s = 0x1f4
>>> s
500
# To convert an int to hex and store it in the hex format
>>> s = hex(500)
>>> s
'0x1f4'
# Note that using `hex(N)` actually creates a string
>>> s = hex(500)
>>> s
'0x1f4'
>>> type(s)
<class 'str'>
# So you can slice a hex number just like any other string.
# Remove the 0x in front of the hex number.
>>> s[2:]
'1f4'
# Convert a hex "string" to int
# You have to specify the base as the second argument
>>> int('0x1f4', 16)
500
# You'll be happy to know that in Python, just like in SV,
# you can use `_` to make large numbers more readable
>>> 0x0001_0000
65536
>>> 0x0000_0000_DEAD_BEEF
3735928559
Working with binary numbers¶
Examples below show how to convert between bin
and int
.
# To create a binary number just like hex, use the bin(N) function
>>> bin(500)
'0b111110100'
# bin(N) also creates a string
# To convert a binary "string" to int
# You have to specify the base as the second argument
>>> int('0b111110100', 2)
500
# Binary numbers can also use _ to make it readable
>>> s = 0b1111_0100_1010_0001
>>> hex(s)
'0xf4a1'
Bit manipulation¶
Bit manipulation in Python works just like in C. Let's consider the following SystemVerilog snippet where we are trying to set bits [7:4]
to 'hA
. The code below shows the equivalent operation in Python.
SystemVerilog v/s Python bit manipulation
/* SystemVerilog */
logic [31:0] interrupt;
initial begin
interrupt = 'hFFFF;
interrupt[7:4] = 'hA;
end
# Python
>>> interrupt = 0xFFFF
>>> interrupt &= ~(0xF << 4) # Clear [7:4]
>>> interrupt |= (0xA << 4) # Set [7:4]
>>> hex(interrupt)
'0xffaf'
Real world use case for ASIC/SoC engineers¶
A fundamental pattern you will see, in SytemVerilog code, is registers (i.e., CSRs) being modified. In the example below, you will see a snippet of code in SystemVerilog and its equivalent implementation in Python.
Take your time with this example and examine the code in SV and Python. Observe the following:
- Both SV and Py use
import
statements to bring in packages into the current scope This is how the defines fromregisters.py
, such asCONFIG_ADDRESS
, are used indv_config.py
. - Notice how Python functions are created using
def <METHOD_NAME()>
, and how arguments are passed to it. The text between'''
is comments, similar to/* */
.
SystemVerilog¶
We have two files
registers.svh
: A header file which contains the registers, fields and address defines. This file could be hand-created, but is typically generated from the register specification (IP-XACT, RDL, etc). For this example, we have a register calledconfig
which has two fieldsmode
andcount
.dv_config.sv
: A testbench class where theconfig
register is accessed within two tasksset_mode_cfg()
andget_count()
.
SystemVerilog: Setting and getting CSR fields
import registers::*;
...
task set_mode_cfg();
config_t cfg = 0;
cfg.mode = 4'h5;
// reg_wr(`CONFIG_ADDRESS, cfg); // Non-UVM
regmodel.config.write(cfg, status); // UVM
endtask
task get_count(output logic[15:0] cnt);
config_t cfg;
// reg_rd(`CONFIG_ADDRESS, cfg); // Non-UVM
regmodel.config.read(cfg, status); // UVM
cnt = cfg.count;
endtask
...
package registers;
`define CONFIG_ADDRESS 32'h1000_0040
`define CONFIG_MODE_FLD_WD 4
`define CONFIG_MODE_FLG_BIT_POS 0
`define CONFIG_MODE_FLD_RANGE 3:0
`define CONFIG_COUNT_FLD_WD 16
`define CONFIG_COUNT_FLD_BIT_POS 4
`define CONFIG_COUNT_FLD_RANGE 23:4
struct packed logic [31:0] {
logic [11:0] reserved;
logic [15:0] count;
logic [3:0] mode;
} config_t;
endpackage
Python¶
- Corresponding to the
*.sv
files above, we haveregisters.py
anddv_config.py
. The comments in the code explain each function. - Code, such as the one below, is typically used during post-silicon validation (lab bringup) or if you have a Python-based config generation flow in your UVM TB.
- I define two convenience functions,
set_field()
andget_field()
, to take care of the bit manipulation. set_mode_cfg()
andget_count()
represent Python equivalents of the SV tasks.
Python: Setting and getting CSR fields
# import the register defines from registers.py
from registers import *
def set_field(reg, fld, pos, len):
'''
Utility function to modify a register and return
updated value
input args:
reg = current register value
fld = field value
pos = starting bit position
len = bit width of the field
returns:
updated `reg` value
'''
mask = 2**len - 1 # For a 5-bit field this will become 0x1F
reg &= ~(mask << pos) # Clear bits associated with the field first
reg |= (fld << pos) # Set the bits associate with that field
return reg
def get_field(reg, pos, len):
'''
Utility function to extract a field from a register
input args:
reg = register value
pos = starting bit position
len = bit width of the field
returns:
extracted `field` value
'''
mask = 2**len - 1 # For a 5-bit field this will become 0x1F
fld = (reg >> pos) & mask
return fld
def set_mode_cfg():
'''
Equivalent to the SystemVerilog task `set_mode_cfg()`
'''
cfg = 0
cfg = set_field(cfg, 0x5, CONFIG_MODE_FLD_BIT_POS, CONFIG_MODE_FLD_WD)
reg_wr(CONFIG_ADDRESS, cfg)
def get_count():
'''
Equivalent to the SystemVerilog task `get_count()`
'''
reg_rd(CONFIG_ADDRESS, cfg)
cnt = get_fld(cfg, CONFIG_COUNT_FLD_BIT_POS, CONFIG_COUNT_FLD_WD)
return cnt
CONFIG_ADDRESS = 0x1000_0040
CONFIG_MODE_FLD_WD = 4
CONFIG_MODE_FLG_BIT_POS = 0
CONFIG_COUNT_FLD_WD = 16
CONFIG_COUNT_FLD_BIT_POS = 4
Random numbers¶
Python offers rich randomization utilities through its in-built random
library. Here is a comparison between common SystemVerilog use cases and Python equivalents.
SystemVerilog | Python | Comments |
---|---|---|
$urandom_range(min, max) |
random.randint(min, max) random.randrange(min, max+1) |
Note that randint(min, max) is equivalent to $urandom_range while randrange() generates min <= N < max it does not include max |
std::randomize(N) |
random.getrandbits(k) |
In order to randomize a variable of arbitrary length, for example logic [47:0] N , in SV we typically use class randomize() . In Python the equivalent is random.getrandbits(k) where k is the number of bits.You could also use random.getrandbytes(K) to get a random number of bytes rather than bits. |
array.shuffle() |
random.shuffle(L) |
Shuffle the elements of a list |
random.choice([1, 2, 3, 4, 5]) |
Choose one random element from a list | |
random.choices([1, 2, 3, 4, 5], k=2) |
Choose k elements from a list WITH repeating of element allowed |
|
random.sample([1, 2, 3, 4, 5], k=2) |
Choose k elements from a list WITHOUT repeating any element |
Let's see these in action.
>>> import random
# Generate a random float: 0.0 <= N < 1.0
>>> random.random()
0.08263888128836916
# Generate a random int: min <= N < max
>>> random.randrange(0, 101)
92
# Generate a random int: min <= N <= max
>>> random.randint(0, 100)
62
# Select random element from a list
>>> random.choice([1,2,3,4,5])
5
# Select `k` random elements from a list, *WITH repetition* allowed
>>> random.choices([1,2,3,4,5], k=3)
[4, 3, 3]
# Select `k` random elements from a list, *WITHOUT repetition*
>>> random.sample([1,2,3,4,5], k=3)
[5, 2, 4]
# Shuffle list in place
>>> s = [1, 2, 3, 4, 5]
>>> random.shuffle(s)
>>> s
[2, 5, 4, 3, 1]
Division¶
# Division always returns a float. You don't have to append a `.0`.
# Like in SV where this has to be 10/5.0 to force output type to be a float.
>>> 10/5
2.0
>>> int(10/5) # force output to be int
2
>> 10//5 # `//` is a short hand for integer division
2
SystemVerilog math functions in Python¶
Commonly used math functions are available through Python's in-built math library. You will have to import math
before using these. This is equivalent to SystemVerilog's import pkg
or C's #include <stdio.h>
.
SystemVerilog | Python | Comment |
---|---|---|
exp(n) |
math.exp(n) |
ex where e=2.71... |
sqrt(n) |
math.sqrt(n) |
|
$clog2(n) |
n = 5000 n.bit_length() |
In SystemVerilog $clog2(DEPTH) is frequently used to calculate the address width. It returns ceil of the log base 2 of the argument. In Python it can be achieved using the in-built .bit_length() function. |
$floor(n) |
math.floor(n) |
|
$ceil(n) |
math.ceil(n) |
|
$pow(m, n) |
m**n math.pow(m, n) |
|
$sin(n) |
math.sin(n) |
|
$cos(n) |
math.cos(n) |
Conclusion¶
That's it for this chapter. You now know how to
- Convert between
hex
,bin
andint
. - Perform bit manipulations
- Generate random numbers
Practice, Practice, Practice¶
- The key to learning Python is practice
- Set 15 minutes on a timer, fire up
python3
on your Linux or MacOS terminal (or use this Python web interpreter) and practice some of the code in this chapter.