Comments (6)
Right. This is a limitation of the Hardcaml simulator. We only support the usual synchronous logic design pattern which implies only using one edge of the clock. It's actually probably not that hard for us to support both edges except
- Simulation performance would effectively halve, without some real effort
- I generally don't like playing games with clocks except when absolutely necessary - simple single clock, single edge semantics make the implementation process very much easier
I still expect we can build something reasonable for a register file though.
open! Import
open Signal
open Hardcaml_waveterm
module I = struct
type 'a t =
{ clock : 'a
; write_enable : 'a
; write_data : 'a [@bits 8]
; write_address : 'a [@bits 5]
; read_address_0 : 'a [@bits 5]
; read_address_1 : 'a [@bits 5]
}
[@@deriving sexp_of, hardcaml]
end
module O = struct
type 'a t =
{ read_data_0 : 'a [@bits 8]
; read_data_1 : 'a [@bits 8]
}
[@@deriving sexp_of, hardcaml]
end
let reg_file
{ I.clock; write_enable; write_data; write_address; read_address_0; read_address_1 }
=
let q =
multiport_memory
32
~write_ports:[| { write_clock = clock; write_address; write_data; write_enable } |]
~read_addresses:[| read_address_0; read_address_1 |]
in
{ O.read_data_0 =
mux2 (write_enable &: (write_address ==: read_address_0)) write_data q.(0)
; read_data_1 =
mux2 (write_enable &: (write_address ==: read_address_1)) write_data q.(1)
}
;;
module Sim = Cyclesim.With_interface (I) (O)
let%expect_test "" =
let sim = Sim.create reg_file in
let waves, sim = Waveform.create sim in
let inputs = Cyclesim.inputs sim in
let step write_address write_data read_address_0 read_address_1 =
inputs.write_address := Bits.of_int ~width:5 write_address;
inputs.write_data := Bits.of_int ~width:8 write_data;
inputs.write_enable := Bits.vdd;
inputs.read_address_0 := Bits.of_int ~width:5 read_address_0;
inputs.read_address_1 := Bits.of_int ~width:5 read_address_1;
Cyclesim.cycle sim;
inputs.write_enable := Bits.vdd
in
step 10 100 0 0;
step 11 101 10 11;
step 12 102 12 11;
Cyclesim.cycle sim;
Waveform.print waves ~display_height:25;
[%expect
{|
┌Signals────────┐┌Waves──────────────────────────────────────────────┐
│clock ││┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌──│
│ ││ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ │
│ ││────────┬───────┬─────────────── │
│read_address_0 ││ 00 │0A │0C │
│ ││────────┴───────┴─────────────── │
│ ││────────┬─────────────────────── │
│read_address_1 ││ 00 │0B │
│ ││────────┴─────────────────────── │
│ ││────────┬───────┬─────────────── │
│write_address ││ 0A │0B │0C │
│ ││────────┴───────┴─────────────── │
│ ││────────┬───────┬─────────────── │
│write_data ││ 64 │65 │66 │
│ ││────────┴───────┴─────────────── │
│write_enable ││──────────────────────────────── │
│ ││ │
│ ││────────┬───────┬─────────────── │
│read_data_0 ││ 00 │64 │66 │
│ ││────────┴───────┴─────────────── │
│ ││────────┬─────────────────────── │
│read_data_1 ││ 00 │65 │
│ ││────────┴─────────────────────── │
│ ││ │
└───────────────┘└───────────────────────────────────────────────────┘ |}]
;;
from hardcaml.
I will update the simulator documentation - this is not said explicitly enough there.
from hardcaml.
Closing this out. There is some explicit new documentation for the simulator features and restrictions which will appear soon.
from hardcaml.
There are 2 types of memory in Hardcaml - asynchronously read, and synchronously read.
The former include the memory
and (newer) multiport_memory
primitives.
The synchronous ones are ram_wbr
and functions in the Ram
module. The notion of read-before-write and write-before-read only exists in the synchronous case.
By synchronous we mean it takes 1 clock cycle to read the data.
I dont think this issue is caused by the simulator, and I suspect the function you want exists as
Ram.create
~collision_mode:Write_before_read ~size:32
~write_ports:[| one_write_port |] ~read_ports:[| two_read_ports |]
That said you say:
However, in our Hardcaml simulator tests, the written data is only read on the next cycle.
That has to be the case for a synchronous read ram (there is a register in the read path - without that you are not describing a RAM that can be implemented on the FPGA). If what you want is an asynchronous read ram, but with write before read you will need to construct that by detecting this case outside the memory ie
let q = async_memory_instantiation .... data_in in
let q = mux2 (memory_is_writing &: write_address ==:. read_address) data_in q in
from hardcaml.
Looking back over our implementation again, I forgot to mention that we made a tweak to ram_wbr
:
let delay_address_to_falling clock address =
let spec = Reg_spec.create ~clock:clock () in
let spec_on_falling = Reg_spec.override ~clock_edge:Edge.Falling spec in
reg ~enable:vdd spec_on_falling address
let regfile rs rt clock write_enable write_address write_data =
let write_port =
{ write_clock = clock; write_address; write_enable; write_data }
in
let number_of_regs = 32 in
let delay_address = delay_address_to_falling clock in
let regs =
multiport_memory number_of_regs ~write_ports:[|write_port|] ~read_addresses:[| delay_address rs; delay_address rt |]
in
(Array.get regs 0, Array.get regs 1)
In particular, instead of using reg_empty
, we configured our read delay register spec to run on falling clock cycles. As a result, the write should occur during the first half of a clock cycle, and the read during the second. The RTL generator represented this correctly:
...
always @(negedge _12) begin
_31 <= _27;
end
always @(posedge _12) begin
if (_6)
_36[_10] <= _8;
end
assign _12 = clock;
assign _32 = _23[4:0];
always @(negedge _12) begin
_35 <= _32;
end
...
And Vivado simulation confirmed that it can read/write as expected in the same clock cycle.
from hardcaml.
Right. This is a limitation of the Hardcaml simulator. We only support the usual synchronous logic design pattern which implies only using one edge of the clock. It's actually probably not that hard for us to support both edges except
- Simulation performance would effectively halve, without some real effort
- I generally don't like playing games with clocks except when absolutely necessary - simple single clock, single edge semantics make the implementation process very much easier
Makes sense, thanks for the clarification! Would it make sense to add a comment discouraging falling edge use to edge.mli?
{ O.read_data_0 =
mux2 (write_enable &: (write_address ==: read_address_0)) write_data q.(0)
; read_data_1 =
mux2 (write_enable &: (write_address ==: read_address_1)) write_data q.(1)
}
We initially planned to explicitly forward writeback => decode as a workaround. This is much neater, thanks!
from hardcaml.
Related Issues (9)
- Installation Error HOT 2
- Use of Variants/Enums for internal signals 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.