Prerequisite Hardcaml interfaces In Hardcaml module hierarchy allows us to partition a design into a tree of sub circuits each generated as its own Verilog or VHDL module when producing RTL output This builds upon instantiation circuit databases s and the design pattern for defining circuits using and interfaces along with a function Module hierarchies provide several important benefits Controls the naming of signals within the design hierarchy Timing reports logic utilization reports from backend vendor tools will be much easier to comprehend Allows better organization of waveforms which can group signals according to the module hierarchy Reduces the size of the generated RTL file where there is substantial logic duplication Provides some options for controlling the backend process of building the final hardware design with vendor tools Scope I O create

A Design Pattern for Circuits

We extend our previous design pattern for circuits to now include a The and interfaces and the function we already know about is new and is what we use to generate structured multi level Hardcaml designs It s mechanically derived as follows The function is built using the functor It provides a function called that takes and and elaborates it according to whether setting of the root scope Flattened Mode The function calls the function of your submodule directly which inlines all the logic into the parent circuit This results in a single flat module with no hierarchy Hierarchical Mode The function generates an instantiation for our subcircuit then elaborates it separately and stores its implementation in a This maintains proper module hierarchy in the generated RTL Scope t open Hardcaml module type Module_design_pattern sig module I Interface S module O Interface S val create Scope t > Signal t I t > Signal t O t val hierarchical Scope t > Signal t I t > Signal t O t end I O create module I struct type a t clock a clear a foo a @bits 8 @@deriving hardcaml end module O struct type a t foo_d a @bits 8 @@deriving hardcaml end # let create _scope Scope t input Signal t I t let spec_with_clear Signal Reg_spec create clock input clock clear input clear in let foo_d Signal reg spec_with_clear enable Signal vdd input foo in O foo_d val create Scope t > Signal t I t > Signal t O t <fun> hierarchical # let hierarchical scope Scope t input Signal t I t let module H Hierarchy In_scope I O in H hierarchical scope name module_name instance module_instance_2 create input val hierarchical Scope t > Signal t I t > Signal t O t <fun> hierarchical Hierarchy In_scope H hierarchical scope create flatten_design flatten_design true create flatten_design false Circuit_database t

Summary

If we follow the design pattern here we must pass a scope to our functions We then use the functor to derive the function When we want to instantiate a subcircuit we call instead of To elaborate a full design we call the top level function with a scope that will set as appropriate As the design is elaborated by calling the functions of subcircuits we will either build a single large fully inlined design or just the top level circuit along with a database containing all the instantiated subcircuits Simulation Here s an example of how we would simulate a hierarchical design RTL generation For RTL generation we need to do a couple of things Create the scope with set to false Construct a Get the circuit database from the scope Pass the circuit database to the function create Hierarchy In_scope hierarchical hierarchical create create flatten_design hierarchical # let create_sim let module Sim Cyclesim With_interface I O in let scope Scope create flatten_design true in Sim create create scope val create_sim unit > Bits t ref I t Bits t ref O t Cyclesim t <fun> flatten_design Circuit Rtl print # let write_verilog let module Circuit Circuit With_interface I O in let scope Scope create flatten_design false in let circuit Circuit create_exn name top create scope in let database Scope circuit_database scope in Rtl print database Verilog circuit val write_verilog unit > unit <fun>