GithubHelp home page GithubHelp logo

janestreet / hardcaml Goto Github PK

View Code? Open in Web Editor NEW
621.0 31.0 39.0 1.87 MB

Hardcaml is an OCaml library for designing hardware.

License: MIT License

Makefile 0.02% OCaml 98.77% C 0.67% Standard ML 0.32% Stata 0.02% Verilog 0.08% VHDL 0.13%

hardcaml's Introduction

"Hardcaml"

Hardcaml is an OCaml library for designing and testing hardware designs.

  • Express hardware designs in OCaml
  • Make generic designs using higher order functions, lists, maps, functors...
  • Simulate designs in OCaml
  • Convert to (hierarchical) Verilog or VHDL
  • Write new modules to transform or analyse circuits, or provide new backends

Install

$ opam install hardcaml ppx_hardcaml hardcaml_waveterm

Documentation

Tools and libraries

  • Hardcaml_waveterm - ASCII based digital waveforms. Usable in expect tests or from an interactive terminal application.
  • Hardcaml_c - convert Hardcaml designs to C-based simulation models. Provides an API compatible with the standard Cyclesim module. Trades compilation time for runtime performance.
  • Hardcaml_verilator - Convert Hardcaml designs to very high speed simulation model using the open source Verilator compiler.
  • Hardcaml_step_testbench - Monadic testbench API. Control multiple tasks synchronized to a clock without converting to a statemachine coding style.
  • Hardcaml_circuits - A library of useful/interesting Hardcaml designs
  • Hardcaml_fixed_point - Fixed point arithmetic with rounding and overflow control
  • Hardcaml_xilinx - Various Xilinx primitives wrapped with Hardcaml interfaces and simulation models
  • Hardcaml_xilinx_components - Tool to read Xilinx unisim and xpm component definitions and generate Hardcaml interfaces
  • Hardcaml_of_verilog - Convert a verilog design to Hardcaml using Yosys
  • Hardcaml_verify - SAT based formal verification tools for Hardcaml
  • Hardcaml_xilinx_reports - Automated generation of synthesis reports from Vivado.

Projects using Hardcaml

hardcaml's People

Contributors

aalekseyev avatar leostera avatar nickdecr avatar public-release avatar staronj avatar xclerc avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

hardcaml's Issues

Simulating write-before-read memory

We're running into a bit of trouble simulating our register file. We'd like to implement it as "write before read", so that we can write to a register in writeback and read from the same register in decode without needing to explicitly forward writeback => decode. By default, it looks like synchronous reads occur before writes. ram_wbr doesn't work because it only supports one read port. We're extended this to 2 read ports, and that works. We've confirmed so by generating RTL and simulating in Vivado.

However, in our Hardcaml simulator tests, the written data is only read on the next cycle. My guess is that this occurs because Cyclesim only reads at the start of every clock cycle, and might be mitigated by reading at every clock edge. We haven't found configuration for evaluating the signal graph twice a cycle though. Is there support for simulating write-before-read memory in Hardcaml?

Failed to install opam core_unix on Amazon EC2

Dear developers, I was trying to translate your codes to obtain the Verilog codes. I set up an Amazon Linux (Inferred) EC2 instance, and successfully installed opam (4.13.1) and other software packages. However, when executing opam install . --deps-only, I got the process Killed. I locate the problematic package is the core_unix. Here's a snapshot of the issue.

[ec2-user@ip-172-31-83-229 hardcaml_zprize]$ opam install core_unix.v0.15.2
The following actions will be performed:
  ∗ install core_unix v0.15.2

<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><>
⬇ retrieved core_unix.v0.15.2  (cached)
Processing  2/3: [core_unix: dune build]Killed

Do you have any idea about this issue? Or, is it possible for me to directly obtain your Verilog files? I hope to understand your work with the Verilog codes since I'm not familiar with the ocaml.

Thanks your time in advance!

Use of Variants/Enums for internal signals

Hi! We're the guys working on a MIPS CPU from the OCaml forums.

We're starting on the decoder/control unit, and we were wondering if there's a way to use variants for internal logic? For context:

Back when we made this in Verilog, our control unit module looked like this. We could convert this pretty much 1:1 to Hardcaml via the Always DSL, but I was wondering if we could leverage OCaml's type system to make the implementation cleaner / more maintainable.

A potential design we came up with looks a bit like this:
image

First, we'd figure out what format and instruction type it is.

Then, we'd use that information to:

  • Generate appropriate control signals for the rest of the datapath.
  • Split the instruction. The destination register is in different places for R-Type and I-Type instructions. And when we start converting this to RISC-V, the immediate value will also be split in different ways depending on the instruction format.

A design like this would make adding/maintaining the various supported instructions a lot simpler.

For simplicity, I'll just talk about the classifier and the splitter units for the remainder of this issue. Thinking about this in terms of OCaml, we'd like to do something like this for implementation:

open Hardcaml
open Hardcaml.Signal

module Parsed_instruction = struct
  type 'a t = {
    rs : 'a; [@bits 5]
    rt : 'a; [@bits 5]
    rdest : 'a; [@bits 5]
    shamt : 'a; [@bits 5]
    imm : 'a; [@bits 32]
  }
  [@@deriving sexp_of, hardcaml]
end

module I = struct
  type 'a t = { instruction : 'a [@bits 32] } [@@deriving sexp_of, hardcaml]
end

module O = struct
  type 'a t = { parsed_instruction : 'a Parsed_instruction.t }
  [@@deriving sexp_of, hardcaml]
end

type rtype_instr = Add | Sub | Sll

type itype_instr = Addi | Lw | Sw

type instr_format = RType of rtype_instr | IType of itype_instr

exception Invalid_instruction

let classify_rtype funct =
  let noop = Sll in
  match funct with
  | f when f == of_string "6'b100000" -> Add
  | f when f == of_string "6'b100010" -> Sub
  | f when f == of_string "6'b000000" -> Sll
  | _ -> noop

let classifier instr =
  let opcode = instr.:[(31, 26)] in
  let noop = RType(Sll) in
  match opcode with
  | o when o == of_string "6'b000000" ->
      let funct = instr.:[(5, 0)] in
      RType (classify_rtype funct)
  | o when o == of_string "6'b001000" -> IType Addi
  | o when o == of_string "6'b100011" -> IType Lw
  | o when o == of_string "6'b101011" -> IType Sw
  | _ -> noop

let splitter instr instr_type =
  let rt = instr.:[(20, 16)] in
  let rd = instr.:[(15, 11)] in
  let rdest = match instr_type with RType _x -> rd | IType _x -> rt in
  let module P = Parsed_instruction in
  {
    P.rs = instr.:[(25, 21)];
    rt;
    rdest;
    shamt = instr.:[(10, 6)];
    imm = sresize instr.:[(15, 0)] 32;
  }

let circuit_impl (_scope : Scope.t) (input : _ I.t) =
  let instr_type = classifier input.instruction in
  let parsed = splitter input.instruction instr_type in
  { O.parsed_instruction = parsed }

This compiles, but doesn't actually work since == doesn't work with signals. The signal comparison operator (==:) doesn't seem to work with the match, since that just outputs another signal.

Looking at the hardcaml source code, we found Enum, which seems like it could support splitter's match via an Enum mux. However, I'm not sure how we could actually generate the enum values in classifier: The only "conditional" primitive for signals seems to be mux, but that can only output Signal.t, and we need to output Signal.t Our_enum.Binary.t.

We'd also eventually like to do something similar with the ALU Control output, but it looks like we can include Signal.t Our_enum.Binary.t entries in Hardcaml interface records, so I believe that if we could generate those enum values in classifier, we could use that same solution for control_core and this would work too.

Is something like this possible in Hardcaml?

Serial multiplier example - can't seem to get it working

I'm trying out the serial multiplier example in the documentation but I can't seem to get the Hardcaml Bits implementation working. I'm using hardcaml v0.14.1 (the one on OPAM).

# open Hardcaml
# open Hardcaml.Bits
# let rec umul' a b =
        if to_int b = 0 
        then 
            zero (width a)
        else
            let partial_product = mux2 b.:[0,0] a (zero (width a)) in
            partial_product +: umul' (sll a 1) (srl b 1)
val umul' : t -> t -> t = <fun>
# let umul a b = umul' (uresize a (width a + width b)) b
val umul : t -> t -> t = <fun>
# to_int (umul (of_int ~width:2 3) (of_int ~width:3 5));;
- : int = 21

The last line be evaluating to 15 (which is 3 * 5), but it is instead evaluating to 21!
The issue seems to be arising from the use of b.:[0,0] and mux2, where every iteration of the algorithm is being added to the sum, not only those where b mod 2 = 1.
I have tested it with this simple example below:

# let b = of_int ~width:2 2
val b  : t = 10
# b. :[0,0]
- : t = 0
# mux2 b.:[0,0] (of_int ~width:1 1) (of_int ~width:1 0)
- : t = 1

and the issue still seems to arise.

Is there something I'm missing here?

Add "debug" signals to circuits

One of the few downsides of Hardcaml as opposed to Verilog+Vivado is the inability to simulate the internals of a circuit. Currently, Circuit.t allows accessing input and output signals, but not anything in between. As a result, only inputs and outputs of circuits can be monitored in waveforms. This can make it difficult to both debug why an implementation isn't working during development, and to create integration tests for some part of a design that can't easily be expressed as an input or output of the total system.

For an example of the latter, see the datapath and CPU components of our Hardcaml MIPS project. writeback_data and writeback_pc are used in integration tests to confirm that some representative instruction sequences are processed correctly. However, they are not used directly higher up in the design, and so aren't really outputs, but we needed to make them outputs anyways so we could test them.

I would like to propose that Hardcaml add optional "debug signals" to circuits. These could be arbitrary signals (or functions of signals), as long as all their leaf dependencies are inputs to the circuit's computation graph. They would be evaluated during simulation and included in waveforms, but pruned during RTL generation to avoid any performance implications. There could also be an option to keep them in the generated RTL in case a different simulation engine is used.

I'm not 100% sure as to an ergonomic way of supporting this in hierarchical circuits, but several ideas are:

  • Registering them to the scope argument that's provided to all implementations of hierarchical functions.
  • Providing a new optional debug argument to implementation functions.
  • Adding an interface for them as an additional functor argument, and having hierarchical circuit implementations return a tuple of (output, debug). This would break current code, or require separate functions/functors, which would be messy.
  • Using some global log function, then inspecting the dependency tree to figure out between which input/output signals it corresponds to. This is my least favorite option, since its very magicky, and I suspect it wouldn't be possible to uniquely identify this.

Beyond this, there could also be a print function that, when given a signal, clock cycle number, and optional message, would print the signal value and message at the given clock cycle in expect tests. I'm not sure how useful this would be if we had debug signals though.

I think this feature could significantly speed up development of new Hardcaml designs by allowing "printf" debugging of sorts, and help increase meaningful test coverage of existing ones.

Documentation

Is there any written documentation on this package, esp.

  • syntax of the DSL
  • how to use the tools
  • ...
    ?
    The only information I was able to find was from A. Ray's talk ("OCaml All the Way Down"). Very, very interesting.
    Also, are the demo code used in this talk available s/w ?

Help with design low-level HDL language

FPGA world suffers a lot from fragmentation - some tools produce Verilog, some VHDL, some - only subsets of them, creating low-level LLVM-like alternative will help everyone, so HDL implementations will opt only for generating this low-level HDL and routing/synthesizers accept it. LLVM or WebAssembly - you can see how many languages and targets are supported now by both. With more open source tools for FPGA this is more feasible now than ever.

See f4pga/ideas#19

Installation Error

while Install getting this error

$ opam install hardcaml
#=== ERROR while installing hardcaml.1.2.0 ====================================#
# opam-version 1.2.2
# os           linux
# command      ocaml pkg/pkg.ml build --with-ctypes false --with-ctypes-foreign false --with-camlp4 true --with-js_of_ocaml true --with-lwt true
# path         /home/abhishek/.opam/system/build/hardcaml.1.2.0
# compiler     system (4.06.0)
# exit-code    1
# env-file     /home/abhishek/.opam/system/build/hardcaml.1.2.0/hardcaml-29565-6d07ae.env
# stdout-file  /home/abhishek/.opam/system/build/hardcaml.1.2.0/hardcaml-29565-6d07ae.out
# stderr-file  /home/abhishek/.opam/system/build/hardcaml.1.2.0/hardcaml-29565-6d07ae.err
### stdout ###
# [...]
# ocamlfind ocamldep -package bytes -package astring -modules src/utils.ml > src/utils.ml.depends
# ocamlfind ocamldep -package bytes -package astring -modules src/comb.mli > src/comb.mli.depends
# ocamlfind ocamlc -c -g -bin-annot -safe-string -package bytes -package astring -I src -I dynlink -I syntax -I js -I csim -o src/comb.cmi src/comb.mli
# ocamlfind ocamldep -package bytes -package astring -modules src/comb.ml > src/comb.ml.depends
# ocamlfind ocamldep -package bytes -package astring -modules src/bits.mli > src/bits.mli.depends
# ocamlfind ocamlc -c -g -bin-annot -safe-string -package bytes -package astring -I src -I dynlink -I syntax -I js -I csim -o src/bits.cmi src/bits.mli
# + ocamlfind ocamlc -c -g -bin-annot -safe-string -package bytes -package astring -I src -I dynlink -I syntax -I js -I csim -o src/bits.cmi src/bits.mli
# File "src/bits.mli", line 67, characters 33-48:
# Error: Unbound module Big_int
# Command exited with code 2.
### stderr ###
# pkg.ml: [ERROR] cmd ['ocamlbuild' '-use-ocamlfind' '-classic-display' '-j' '4' '-tag' 'debug'
# [...]
#      'src/transform.mli' 'src/fixed.mli' 'src/rtl.mli' 'src/circuit.mli'
#      'src/signal.mli' 'src/bits.mli' 'src/comb.mli' 'src/utils.mli'
#      'dynlink/HardCamlDynlink.cmxs' 'dynlink/HardCamlDynlink.cmxa'
#      'dynlink/HardCamlDynlink.cmx' 'dynlink/HardCamlDynlink.cma'
#      'dynlink/HardCamlDynlink.cmi' 'dynlink/HardCamlDynlink.a'
#      'dynlink/sim_provider.mli' 'js/HardCamlJS.cmxs' 'js/HardCamlJS.cmxa'
#      'js/HardCamlJS.cmx' 'js/HardCamlJS.cma' 'js/HardCamlJS.cmi'
#      'js/HardCamlJS.a' 'js/wave.mli' 'syntax/pa_hardcaml.cmi'
#      'syntax/pa_hardcaml.cmx' 'syntax/pa_hardcaml.cmo']: exited with 10



=-=- Error report -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

Document Required

I am little clueless now...
Document or a quick start guide would be helpful.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.