-
Notifications
You must be signed in to change notification settings - Fork 14
Module
PyHCL's module is very similar to Verilog's module in functional. A module is a hierarchical circuit structure which contain specific circuit logic. However, PyHCL's module supports object oriented feature. A PyHCL module could be inherited, and support for polymorphism and encapsulation.
There are several rules when users define a module:
- The module must be a class inherit base class
Module
, - The module must contain I/O ports definition, that is, using
IO
API to define the I/O ports of the module,
These two rules are the basic and necessary for a module, but we also give several suggestions for module definition:
- Make sure all output ports of the module are well connected,
- Try not to define redundant elements in module, although the PyHCL and FIRRTL compiler may optimize the circuit logic and would not reflected to the result code, make your code more concise is always a good choice.
For example, we defined a simple ALU module which only support four arithmatic operations:
class ALU(Module):
io = IO(
a=Input(U.w(32)),
b=Input(U.w(32)),
ctl=Input(U.w(2)),
out=Output(U.w(32)),
)
io.out <<= LookUpTable(io.ctl, {
ALU_Op.ALU_ADD: io.a + io.b,
ALU_Op.ALU_SUB: io.a - io.b,
ALU_Op.ALU_MUL: io.a * io.b,
ALU_Op.ALU_DIV: io.a / io.b,
...: U(0)
})
ALU
inherits Module
indicate that it is a standard PyHCL module. Then we define several I/O ports for ALU
using the IO
API. The port out
would output different values according to the condition from the input port ctl
. The module above uses LookUpTable
for condition selection, we would describe in the advanced topics, you only need to know it is a pre-defined PyHCL component now.
Using a pre-defined module in another module is very common when design a complex circuit. Also, when you dealing with a massive projects, you must need the hierarchy of the modules. In PyHCL, module is actually a Python class, so using a module in another module is simple. The way is totally the same as instantiate a Python class. For example, we want our ALU support another two operations: bitwise AND and OR.
class eALU(Module):
io = IO(
a=Input(U.w(32)),
b=Input(U.w(32)),
ctl=Input(U.w(32)),
out=Output(U.w(32))
)
base_alu = ALU()
base_alu.io.a <<= io.a
base_alu.io.b <<= io.b
base_alu.io.ctl <<= io.ctl
with when(io.ctl <= ALU_Op.ALU_DIV):
io.out <<= base_alu.io.out
with otherwise():
io.out <<= LookUpTable(io.ctl, {
ALU_Op.ALU_AND: io.a & io.b,
ALU_Op.ALU_OR: io.a | io.b,
...: U(0)
})
when
and otherwise
statements are implemented in PyHCL library, we would describe them in section 9. ALU_Op
holds the literal values represent the ALU's control signals. We assume they increase by the order of addition, subtraction, ..., and so on. When instantiate a module in another module, we must connect all the I/O ports of the module. Actually, we could separate the io
definision and make the code more concise.
There is another way to extend our ALU to support another two operations. That is inherited the ALU
. As we said before that a module is a standard Python class, so we also could inherit it:
class iALU(ALU):
io.out <<= LookUpTable(io.ctl, {
ALU_Op.ALU_ADD: io.a + io.b,
ALU_Op.ALU_SUB: io.a - io.b,
ALU_Op.ALU_MUL: io.a * io.b,
ALU_Op.ALU_DIV: io.a / io.b,
ALU_Op.ALU_AND: io.a & io.b,
ALU_Op.ALU_OR: io.a | io.b,
...: U(0)
})
If we re-connect the same port or element, what would gonna happen when compile to FIRRTL or Verilog? The answer is simple, the later connection would overwrite the connection before. So the latest connection would be compile to FIRRTL and Verilog code.
<<Prev(Circuit Elements) >>Next(Case Study: Combinational Circuits)