Comments (6)
Here's some usage examples (in today's world, and the updated version coming soon). that should cover your use cases Let's say we have the following Enum:
open Hardcaml
open Signal
module Instruction_type = struct
module Enum = struct
type t = Add | Sub | Mulu | Noop
[@@deriving compare, enumerate, sexp_of, variants]
end
include Interface.Make_enums(Enum)
end
Arbitrary Signal Logic -> Enum
There is a few ways you can get this. This will not change when the Enum update lands.
- Create a multiplexer with
Instruction_type.Binary.Of_signal.mux
that returns Noop in cases you care about.
let decode (x : Signal.t) =
let num_bits = Signal.width x in
assert (num_bits = 6);
let f = function
| 0b000000 -> Instruction_type.Enum.Add
| 0b000100 -> Sub
| 0b001000 -> Mulu
| _ -> Noop
in
List.init (1 lsl num_bits) (fun i -> Instruction_type.Binary.of_enum (module Signal) (f i))
|> Instruction_type.Binary.Of_signal.mux x
;;
- Chaining mux (this is explicitly just creating a priorioty mux)
(* Note that (==:.) has the type signature of Signal.t -> int -> Signal.t *)
let decode x =
let mux2 s t f = Instruction_type.Binary.Of_signal.mux2 s (Instruction_type.Binary.of_enum (module Signal) t) f in
mux2 (x ==:. 0b0000) Add
@@ mux2 (x ==:. 0b0100) Sub
@@ mux2 (x ==:. 0b1000) Mulu
@@ Instruction_type.Binary.of_enum (module Signal) Noop
;;
Both ways yields different architectures, so it depends what you want here. You could also consider using Instruction_type.Binary.Of_signal.priority_select_with_default
.
Raw Binary Representation -> Enum
(I suspect you won't need this once the update is out, since you can work exclusively in the typed representation.)
In the current version in github:
utop # Instruction_type.Binary.Of_signal.unpack
;;
- : ?rev:bool -> Signal.t -> Signal.t Instruction_type.Binary.t = <fun>
In the coming update, we will add something that is more obvious, ie:
let encoded = Instruction_type.Binary.of_raw (module Signal) (Signal.of_int ~width:3 6)
Enum -> Arbitrary Signal logic
What you can do right now is:
let a =
Instruction_type.Binary.mux (module Signal)
~default:(of_int ~width:3 2)
[ Add, of_int ~width:3 0
; Sub, of_int ~width:3 1
]
;;
The confusing bit here is that Instruction_type.Binary.mux
refers to a multiplexer that uses the enum as a selector, whereas Instruction_type.Binary.Of_signal.mux
is a multiplexer that uses as a selector, and returns a Signal.t
In the coming update, we added several functions to make using enums easier (along with documentation). Namely:
- Renaming
Instruction_type.mux
intoInstruction_type.match_
to more explicitly distinguishInstruction_type.mux
fromInstruction_type.Of_signal.mux
. There will also beInstruction_type.Of_signal.match_
if you prefer not to pass a first-class argument. Instruction_type.Of_always.match_
which allows you to write a switch-like statements on enums.Instruction_type.Of_signal.is : Instruction_type.t -> Instruction_type.Enum.t -> Signal.t
so you can simply write things likemux2 (Instruction_type.Of_signal.is Add) ...
- Some utilities to make writing testbenches Enum I/O ports easier.
from hardcaml.
Some utilities to make writing testbenches Enum I/O ports easier.
Out of curiosity, have you considered something that would display a human-readable name for enum interfaces in generated waveforms? That would be pretty useful for debugging, although there'd probably be some complications with wave width consistency, and I'm not sure if that could be implemented without breaking abstraction.
I think we should add something for this, though I am pretty sure it can be done now anyway.
Display_rules lets you define custom printers in the waveform. The Custom
one takes a Bits.t -> string
Its not trivial to convert Bits.t Enum.t -> Bits.t -> string, but can be done.
We should add a util that helps with this.
One of my colleagues recommended specifying a wave format when we declare a signal (ie like names currently). I don't think I am convinced this is the right idea just yet, but also, dont be surprised if we do it in the end.
from hardcaml.
The latest code that supports enums are out! The documentation is available at https://github.com/janestreet/hardcaml/blob/master/docs/enums.mdx
from hardcaml.
You cannot use OCamls match statement in the same way you cannot use OCamls if statement.
I think enums are a good option here - I am not totally sure what operation you cant perform - could you provide an explicit example? My colleague who wrote the Enum module has written some documentation for it and taken a swipe at the API to simplify it a bit. That may help, but it will take a couple of weeks for that work to land in the repository.
Generally, however, I think a pattern of defining a module with an abstract type and API around that type is the way we attack this problem (Enum is basically this pattern generalized for simple variant types).
At a high level every type you mention in your code example would lead to a module and type:
module Rtype_intstr : sig
type t
val do_some_muxing_thing : t -> ....
end = struct
...
end
You will sometimes need to break abstraction by providing to_signal
or of_signal
, though not always.
from hardcaml.
You cannot use OCamls match statement in the same way you cannot use OCamls if statement.
Makes sense, thank you!
For now, we tried using the Always DSL for switch logic and a set of const values for the variants. It works pretty well. The only thing that's missing compared to the (impossible) match-based code is some form of exhaustivity check. Also, having to manually specify rank for each item is a bit messy (especially as we add more instructions).
could you provide an explicit example?
Sorry for the vagueness in the original post. I think the part I'm not getting is of_signal
: can a function that takes only signal inputs return something that isn't a signal? We haven't found a way so far, but we might be looking in the wrong place.
For an example of where we got stuck working with Enum
, let's say we have an Enum
type for some basic instruction types:
module Instruction_type = struct
type t = Add | Sub
[@@deriving compare, enumerate, sexp_of, variants]
end
module Enum_instruction_type = Interface.Make_enums(Instruction_type)
Now, we want to use this as an intermediate format when mapping instruction opcodes/functs to some control outputs (for now, just the ALU control). We probably need the following 2 functions:
val instruction_to_instruction_type : Signal.t -> Signal.t Instruction_type_enum.Binary.t
val instruction_type_to_alu_control : Signal.t Instruction_type_enum.Binary.t -> Signal.t
For instruction_type_to_alu_control
, I'm guessing we could use an Always DSL switch, with:
Instruction_type_enum.Binary.to_raw INPUT
to get the selectorInstruction_type_enum.Binary.to_raw (Instruction_type_enum.Binary.of_enum (module Signal) Instruction_type.Add)
for each of the cases.
However, I'm not sure how we could implement instruction_to_instruction_type
, as we couldn't find any functions from Signal.t
to Signal.t Instruction_type_enum.Binary.t
. Instruction_type_enum.Binary.to_enum
won't work here, because that requires the input to be Bits.t
.
On a side note, an even cleaner theoretical implementation could be:
val instruction_to_instruction_type instruction =
let funct = instruction.:[(5, 0)] in
...
let instruction_type_to_alu_control instr_type =
match instr_type with
| Instruction_type.Add -> of_string "4'b0000"
| Instruction_type.Sub -> of_string "4'b0001"
But I'm assuming we can't have;
val instruction_to_instruction_type : Signal.t -> Instruction_type.t
as that would open the door to all sorts of unsynthesizable logic.
from hardcaml.
Create a multiplexer with Instruction_type.Binary.Of_signal.mux that returns Noop in cases you care about.
Ah, I think I understand this now. Comb
is a functor that provides functions for handling any interface. This includes packing/unpacking to/from Signal
/Bit
, muxing from Signal
/Bit
to an interface instance, etc. And because Enums are ppx
ed into interface-compliant modules, we can use these functions with our enums. So essentially, we've gone from a set of operations over Signal.t
to a set of operations over Signal.t
and Signal.t SOME_INTERFACE.t
. That's really cool!
It also looks like that first example could be adapted for an Signal.t ENUM_A.Binary.t -> Signal.t ENUM_B.Binary.t
mapping function, with something like:
let decode instr_type =
let num_bits =
Signal.width
(Instruction_type.Binary.to_raw
(Instruction_type.Binary.of_enum
(module Signal)
Instruction_type.Enum.Add))
in
let f = function
| Instruction_type.Enum.Add -> Alu_op.Enum.Add
| Instruction_type.Enum.Sub -> Alu_op.Enum.Sub
| Instruction_type.Enum.Lw -> Alu_op.Enum.Add
| Instruction_type.Enum.Sw -> Alu_op.Enum.Add
in
let all_types = Array.of_list Instruction_type.Enum.all in
let type_rank_to_op i =
Alu_op.Binary.of_enum
(module Signal)
(f (Array.get all_types i))
in
List.init (1 lsl num_bits) type_rank_to_op
|> Alu_op.Binary.Of_signal.mux (Instruction_type.Binary.to_raw instr_type)
So this covers our use case very well, and gives us exhaustivity! One suggestion I have would be surfacing width
in the S_enum
signature. It seems to already be defined for both Binary
and One_hot
, and would make the logic above a bit simpler.
In the coming update, we added several functions to make using enums easier (along with documentation).
Really excited for this!
Some utilities to make writing testbenches Enum I/O ports easier.
Out of curiosity, have you considered something that would display a human-readable name for enum interfaces in generated waveforms? That would be pretty useful for debugging, although there'd probably be some complications with wave width consistency, and I'm not sure if that could be implemented without breaking abstraction.
from hardcaml.
Related Issues (9)
- Installation Error HOT 2
- Simulating write-before-read memory HOT 6
- Add "debug" signals to circuits HOT 1
- Failed to install opam core_unix on Amazon EC2 HOT 1
- Document Required HOT 2
- Help with design low-level HDL language HOT 3
- Documentation HOT 2
- Serial multiplier example - can't seem to get it working HOT 7
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from hardcaml.