├── .gitignore ├── .vscode └── launch.json ├── Cargo.toml ├── LICENSE ├── README.md ├── changelog.md ├── docs ├── FFTtutorial121102.pdf └── ideas.md ├── guide ├── .gitignore ├── README.md ├── book.toml └── src │ ├── README.md │ ├── SUMMARY.md │ ├── bits.md │ ├── chapter_1.md │ └── chapter_2.md ├── install_toolchain_notes.md ├── rust-hdl-bsp-alchitry-cu ├── Cargo.toml ├── src │ ├── lib.rs │ ├── pins.rs │ └── synth.rs └── tests │ ├── bsp_alchitry_cu_icepll.rs │ ├── bsp_alchitry_cu_pulser.rs │ ├── bsp_alchitry_cu_pulser_pll.rs │ ├── bsp_alchitry_cu_pwm.rs │ ├── bsp_alchitry_cu_pwm_vec.rs │ └── bsp_alchitry_cu_pwm_vec_srom.rs ├── rust-hdl-bsp-ok-xem6010 ├── Cargo.toml ├── src │ ├── lib.rs │ └── xem6010 │ │ ├── ddr_fifo.rs │ │ ├── mcb_if.rs │ │ ├── mig.rs │ │ ├── mod.rs │ │ ├── ok_download_ddr.rs │ │ ├── pins.rs │ │ ├── pll.rs │ │ └── synth.rs └── tests │ ├── bsp_ok_xem6010_blinky.rs │ ├── bsp_ok_xem6010_ddr.rs │ ├── bsp_ok_xem6010_download.rs │ ├── bsp_ok_xem6010_download32.rs │ ├── bsp_ok_xem6010_fir.rs │ ├── bsp_ok_xem6010_hls_bridge.rs │ ├── bsp_ok_xem6010_i2c_rs │ ├── bsp_ok_xem6010_mig.rs │ ├── bsp_ok_xem6010_mux_spi.rs │ ├── bsp_ok_xem6010_pipe_afifo.rs │ ├── bsp_ok_xem6010_pipe_bt.rs │ ├── bsp_ok_xem6010_pipe_fifo.rs │ ├── bsp_ok_xem6010_pipe_in.rs │ ├── bsp_ok_xem6010_pipe_ram.rs │ ├── bsp_ok_xem6010_pll.rs │ ├── bsp_ok_xem6010_sdram_fifo_tester.rs │ ├── bsp_ok_xem6010_soc_test.rs │ ├── bsp_ok_xem6010_spi.rs │ ├── bsp_ok_xem6010_tristate.rs │ ├── bsp_ok_xem6010_wave.rs │ └── bsp_ok_xem6010_wire.rs ├── rust-hdl-bsp-ok-xem7010 ├── Cargo.toml ├── src │ ├── lib.rs │ └── xem7010 │ │ ├── ddr_fifo7.rs │ │ ├── download.rs │ │ ├── mcb_if.rs │ │ ├── mig7.rs │ │ ├── mod.rs │ │ ├── pins.rs │ │ ├── synth.rs │ │ └── sys_clock.rs └── tests │ ├── bsp_ok_xem7010_blinky.rs │ ├── bsp_ok_xem7010_ddr.rs │ ├── bsp_ok_xem7010_ddr_fifo.rs │ ├── bsp_ok_xem7010_download.rs │ ├── bsp_ok_xem7010_download32.rs │ ├── bsp_ok_xem7010_fast_blinky.rs │ ├── bsp_ok_xem7010_mig.rs │ ├── bsp_ok_xem7010_mux_spi.rs │ ├── bsp_ok_xem7010_pipe.rs │ ├── bsp_ok_xem7010_pipe_afifo.rs │ ├── bsp_ok_xem7010_pipe_fifo.rs │ ├── bsp_ok_xem7010_pipe_in.rs │ ├── bsp_ok_xem7010_pipe_ram.rs │ ├── bsp_ok_xem7010_spi.rs │ ├── bsp_ok_xem7010_wave.rs │ └── bsp_ok_xem7010_wire.rs ├── rust-hdl-core ├── Cargo.toml ├── src │ ├── ast.rs │ ├── atom.rs │ ├── bits.rs │ ├── bitvec.rs │ ├── block.rs │ ├── check_connected.rs │ ├── check_error.rs │ ├── check_logic_loops.rs │ ├── check_timing.rs │ ├── check_write_inputs.rs │ ├── clock.rs │ ├── code_writer.rs │ ├── constant.rs │ ├── constraint.rs │ ├── direction.rs │ ├── lib.rs │ ├── logic.rs │ ├── module_defines.rs │ ├── named_path.rs │ ├── path_tools.rs │ ├── prelude.rs │ ├── probe.rs │ ├── short_bit_vec.rs │ ├── signal.rs │ ├── signed.rs │ ├── simulate.rs │ ├── synth.rs │ ├── timing.rs │ ├── top_wrap.rs │ ├── type_descriptor.rs │ ├── vcd_probe.rs │ ├── verilog_gen.rs │ ├── verilog_visitor.rs │ └── yosys.rs └── tests │ ├── addnum_test.rs │ ├── bitcast_test.rs │ └── struct_valued.rs ├── rust-hdl-fpga-support ├── Cargo.toml └── src │ ├── lattice │ ├── ecp5 │ │ ├── edge_flip_flop.rs │ │ ├── edge_tristate_buffer.rs │ │ ├── edge_tristate_buffer_delayed.rs │ │ ├── io_delay.rs │ │ ├── mod.rs │ │ ├── oddr.rs │ │ └── output_buffer.rs │ ├── ice40 │ │ ├── ice_pll.rs │ │ └── mod.rs │ └── mod.rs │ ├── lib.rs │ └── toolchains │ ├── ecp5.rs │ ├── icestorm.rs │ ├── ise.rs │ ├── mod.rs │ └── vivado.rs ├── rust-hdl-hls ├── Cargo.toml └── src │ ├── bidi.rs │ ├── bridge.rs │ ├── bus.rs │ ├── controller.rs │ ├── cross_fifo.rs │ ├── expander.rs │ ├── fifo.rs │ ├── fifo_linker.rs │ ├── host.rs │ ├── lib.rs │ ├── miso_fifo_port.rs │ ├── miso_port.rs │ ├── miso_wide_port.rs │ ├── mosi_fifo_port.rs │ ├── mosi_port.rs │ ├── mosi_wide_port.rs │ ├── prelude.rs │ ├── reducer.rs │ ├── router.rs │ ├── router_rom.rs │ ├── sdram_controller.rs │ ├── sdram_controller_tester.rs │ ├── sdram_fifo.rs │ ├── sim.rs │ ├── spi.rs │ └── test_helpers.rs ├── rust-hdl-macros ├── .gitignore ├── Cargo.toml └── src │ ├── common.rs │ ├── connect_gen.rs │ ├── hdl_gen.rs │ ├── lib.rs │ ├── logic_block.rs │ ├── logic_interface.rs │ ├── logic_state.rs │ └── logic_struct.rs ├── rust-hdl-ok-core ├── Cargo.toml └── src │ ├── core │ ├── bsp.rs │ ├── clock.rs │ ├── mod.rs │ ├── ok_download.rs │ ├── ok_hi.rs │ ├── ok_hls_bridge.rs │ ├── ok_host.rs │ ├── ok_pipe.rs │ ├── ok_trigger.rs │ ├── ok_wire.rs │ ├── prelude.rs │ ├── spi.rs │ └── tools.rs │ ├── lib.rs │ └── test_common │ ├── blinky.rs │ ├── core_mig.rs │ ├── ddr.rs │ ├── download.rs │ ├── fifo_tester.rs │ ├── fir.rs │ ├── mod.rs │ ├── mux_spi.rs │ ├── pipe.rs │ ├── prelude.rs │ ├── soc.rs │ ├── spi.rs │ ├── tools.rs │ ├── wave.rs │ └── wire.rs ├── rust-hdl-ok-frontpanel-sys ├── Cargo.toml ├── build.rs ├── readme.md └── src │ └── lib.rs ├── rust-hdl-org ├── .gitignore ├── README.md ├── babel.config.js ├── docs │ ├── changelog.md │ ├── guide │ │ ├── fpga │ │ │ ├── cybersecurity.md │ │ │ ├── deterministic-systems.md │ │ │ ├── high-speed-io.md │ │ │ ├── index.md │ │ │ ├── kitchen-analogy.md │ │ │ └── low-power.md │ │ ├── index.md │ │ ├── programming │ │ │ ├── index.md │ │ │ ├── lucid.md │ │ │ ├── migen.md │ │ │ └── verilog.md │ │ └── rusthdl │ │ │ ├── bits.md │ │ │ ├── high_level_synthesis.md │ │ │ ├── index.md │ │ │ ├── interfaces.md │ │ │ ├── loops.md │ │ │ ├── operators.md │ │ │ ├── signals.md │ │ │ ├── simulation.md │ │ │ ├── struct_valued.md │ │ │ ├── synthesizable.md │ │ │ ├── traits.md │ │ │ ├── verilog.md │ │ │ └── wrapping.md │ ├── img │ │ ├── 16526-Alchitry_Cu_FPGA_Development_Board__Lattice_iCE40_HX_-03.jpg │ │ ├── blinky_all.svg │ │ └── blinky_pulse.svg │ ├── intro.md │ ├── references.md │ ├── roadmap.md │ └── simulation.md ├── docusaurus.config.js ├── package-lock.json ├── package.json ├── sidebars.js ├── src │ ├── components │ │ └── HomepageFeatures │ │ │ ├── index.tsx │ │ │ └── styles.module.css │ ├── css │ │ └── custom.css │ └── pages │ │ ├── index.module.css │ │ ├── index.tsx │ │ └── markdown-page.md ├── static │ ├── .nojekyll │ └── img │ │ ├── blinky.mp4 │ │ ├── cyber_ferris_batteries.svg │ │ ├── cyber_ferris_power.png │ │ ├── cyber_ferris_powerful.svg │ │ ├── cyber_ferris_safe.svg │ │ ├── docusaurus-social-card.jpg │ │ ├── docusaurus.png │ │ ├── favicon.ico │ │ ├── logo.svg │ │ ├── undraw_docusaurus_mountain.svg │ │ ├── undraw_docusaurus_react.svg │ │ └── undraw_docusaurus_tree.svg └── tsconfig.json ├── rust-hdl-sim ├── Cargo.toml └── src │ ├── ad7193_sim.rs │ ├── ads8688_sim.rs │ ├── ads868x_sim.rs │ ├── lib.rs │ ├── max31856_sim.rs │ ├── muxed_ad7193_sim.rs │ ├── muxed_ads868x_sim.rs │ ├── muxed_max31856_sim.rs │ ├── prelude.rs │ └── sdr_sdram │ ├── bank.rs │ ├── chip.rs │ └── mod.rs ├── rust-hdl-widgets ├── Cargo.toml └── src │ ├── accum.rs │ ├── auto_reset.rs │ ├── bidirectional_bus.rs │ ├── delay_line.rs │ ├── dff.rs │ ├── dff_with_init.rs │ ├── edge_detector.rs │ ├── edge_ff.rs │ ├── fifo │ ├── async_fifo.rs │ ├── cross_fifo.rs │ ├── fifo_expander_n.rs │ ├── fifo_logic.rs │ ├── fifo_reducer.rs │ ├── fifo_reducer_n.rs │ ├── fifo_register.rs │ ├── mod.rs │ └── sync_fifo.rs │ ├── i2c │ ├── i2c_bus.rs │ ├── i2c_controller.rs │ ├── i2c_driver.rs │ ├── i2c_target.rs │ ├── i2c_test_target.rs │ ├── mod.rs │ └── sim.rs │ ├── lib.rs │ ├── mac_fir.rs │ ├── open_drain.rs │ ├── png │ ├── lfsr.rs │ └── mod.rs │ ├── prelude.rs │ ├── pulser.rs │ ├── pwm.rs │ ├── ramrom │ ├── mod.rs │ ├── ram.rs │ ├── rom.rs │ └── sync_rom.rs │ ├── registered_edge_tristate.rs │ ├── sdram │ ├── basic_controller.rs │ ├── buffer.rs │ ├── burst_controller.rs │ ├── cmd.rs │ ├── fifo_sdram.rs │ ├── mod.rs │ └── timings.rs │ ├── shot.rs │ ├── spi │ ├── master.rs │ ├── master_dynamic_mode.rs │ ├── mod.rs │ ├── mux.rs │ └── slave.rs │ ├── strobe.rs │ ├── synchronizer.rs │ └── tristate.rs ├── rust-hdl-x-macro-core ├── Cargo.toml └── src │ ├── kernel.rs │ ├── lib.rs │ └── loggable.rs ├── rust-hdl-x-macro ├── Cargo.toml └── src │ └── lib.rs ├── rust-hdl-x-widgets ├── Cargo.toml └── src │ ├── counter.rs │ ├── lib.rs │ ├── pulser.rs │ ├── reg_fifo.rs │ ├── shot.rs │ └── strobe.rs ├── rust-hdl-x ├── Cargo.toml ├── counter.bcd └── src │ ├── ast.rs │ ├── basic_logger.rs │ ├── basic_logger_builder.rs │ ├── kind.rs │ ├── lib.rs │ ├── log.rs │ ├── loggable.rs │ ├── logger.rs │ ├── probe.rs │ ├── spi_controller.rs │ ├── synchronous.rs │ └── synth.rs └── rust-hdl ├── .gitignore ├── Cargo.toml ├── README.md ├── doc ├── md │ ├── banner.md │ ├── bits.md │ ├── bits_operations.md │ ├── features.md │ ├── quickstart.md │ └── signals_intro.md ├── notes.md └── svg │ ├── blinky_all.svg │ └── blinky_pulse.svg ├── images ├── blinky_all.svg ├── blinky_pulse.svg └── my_adder.svg ├── src ├── docs │ ├── mod.rs │ └── vcd2svg │ │ ├── display_metrics.rs │ │ ├── interval.rs │ │ ├── mod.rs │ │ ├── renderable.rs │ │ ├── symbols.rs │ │ ├── text_frame.rs │ │ ├── time_view.rs │ │ ├── timed_value.rs │ │ ├── trace_collection.rs │ │ ├── utils.rs │ │ └── vcd_style.rs ├── fig.md ├── lib.rs └── prelude.rs └── tests ├── core_base_tests.rs ├── core_dff_with_init.rs ├── core_edge_detector.rs ├── core_expander.rs ├── core_fifo.rs ├── core_fifo_link.rs ├── core_fifo_sdram.rs ├── core_fir_test.rs ├── core_hls_bidi.rs ├── core_hls_controller.rs ├── core_hls_cross_fifo.rs ├── core_hls_expander_reducer.rs ├── core_hls_fifo.rs ├── core_hls_host.rs ├── core_hls_miso_port.rs ├── core_hls_mosi_port.rs ├── core_hls_router.rs ├── core_hls_router_stack.rs ├── core_hls_sdram_controller_test.rs ├── core_hls_sdram_controller_tester.rs ├── core_hls_sdram_fifo.rs ├── core_hls_spi.rs ├── core_hls_spi_dm.rs ├── core_hls_spi_mux.rs ├── core_hls_spi_mux_slaves.rs ├── core_hls_testchip.rs ├── core_lfsr_widget.rs ├── core_nested_ports.rs ├── core_pwm.rs ├── core_ram.rs ├── core_reducer.rs ├── core_reg_fifo.rs ├── core_rom.rs ├── core_sdram.rs ├── core_sdram_burst.rs ├── core_signed.rs ├── core_soc.rs ├── core_spi.rs ├── core_spi_mux.rs ├── core_struct_valued.rs ├── core_sync_reset.rs ├── core_sync_rom.rs ├── core_timing.rs └── core_tristate_test.rs /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | **/target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | **/*.vcd 13 | **/*.v 14 | 15 | # Working dir for jetbrains IDEs 16 | .idea 17 | 18 | # Mac stuff 19 | .DS_Store 20 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | members = [ 4 | "rust-hdl", 5 | "rust-hdl-core", 6 | "rust-hdl-hls", 7 | "rust-hdl-sim", 8 | "rust-hdl-widgets", 9 | "rust-hdl-macros", 10 | "rust-hdl-ok-core", 11 | "rust-hdl-fpga-support", 12 | "rust-hdl-bsp-alchitry-cu", 13 | "rust-hdl-bsp-ok-xem6010", 14 | "rust-hdl-bsp-ok-xem7010", 15 | "rust-hdl-x", 16 | "rust-hdl-x-widgets", 17 | "rust-hdl-x-macro", 18 | "rust-hdl-x-macro-core", 19 | ] 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Original work Copyright (c) 2021 Smiths Digital Forge 4 | Modified work Copyright (c) 2023 Samit Basu 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /docs/FFTtutorial121102.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samitbasu/rust-hdl/1d46ea9e82d2f66d76b41333f7ec4f66bdde122f/docs/FFTtutorial121102.pdf -------------------------------------------------------------------------------- /guide/.gitignore: -------------------------------------------------------------------------------- 1 | book 2 | -------------------------------------------------------------------------------- /guide/README.md: -------------------------------------------------------------------------------- 1 | # FPGA Programming with RustHDL 2 | 3 | # Building 4 | 5 | Building requires mdbook, which can be installed from crates.io: 6 | 7 | ```sh 8 | cargo install mdbook 9 | ``` 10 | 11 | Assuming you've placed the install directory `~/.cargo/bin` into your system PATH, then run from the root of your local copy: 12 | 13 | ```sh 14 | mdbook build 15 | ``` 16 | 17 | --- 18 | 19 | If you'd prefer, this project can also be built with 20 | [GitBook](https://github.com/GitbookIO/gitbook), although GitBook 21 | is not officially supported and compatibility is therefore 22 | uncertain and incidental. 23 | -------------------------------------------------------------------------------- /guide/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | language = "en" 3 | multilingual = false 4 | src = "src" 5 | title = "FPGA Programming With RustHDL" 6 | author = "Samit Basu" 7 | description = "FPGA Programming with RustHDL" 8 | 9 | [output.html] 10 | git-repository-url = "https://github.com/samitbasu/rust-hdl/tree/main/guide" 11 | -------------------------------------------------------------------------------- /guide/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [Chapter 1](./chapter_1.md) 4 | - [Chapter 2](./chapter_2.md) 5 | - [Chapter 3](./bits.md) 6 | -------------------------------------------------------------------------------- /guide/src/bits.md: -------------------------------------------------------------------------------- 1 | # BitVectors 2 | 3 | The base of the RustHDL classes is the notion of a bitvector. 4 | A bitvector is a way to have a statically sized vector of boolean digits 5 | that can be used much like an atomic, built in integer class in RustHDL. 6 | The bitvector is important to understand because it forms the basic 7 | type that underlies RustHDL. Generally, bitvectors are designed 8 | to be intuitive. 9 | 10 | ## Constructing Bits from a Constant 11 | 12 | If you hae a value that you know is small enough to fit into Rust's 13 | base type (a `u128` at the moment), then you can use the `bits` 14 | function to construct it: 15 | 16 | ```rust 17 | #fn main() { 18 | let t = bits::<13>(5_u128); // t is 13 bits wide, and represents 5 19 | #} 20 | ``` 21 | 22 | You can also cast from one bits to another. Bit casting is very common 23 | in HDL designs. RustHDL makes the cast explicit by requiring you 24 | to satisfy the type system. Here is the declaration of the 25 | `bit_cast` function: 26 | 27 | ```rust 28 | pub fn bit_cast(x: Bits) -> Bits { 29 | // Snip... 30 | } 31 | ``` 32 | 33 | If you aren't familiar with const generics, the syntax may look a little 34 | odd, but it's basically parameterized by the input and output widths. 35 | In some cases, you won't need to provide these, since Rust will infer 36 | them from context. For example: 37 | 38 | ```rust 39 | #fn main() { 40 | let x : Bits<4> = bit_cast(bits::<6>(5_u128)); // x is 5 represented with 4 bits 41 | #} 42 | ``` 43 | 44 | You can also use the turbofish notation `::<>` and indicate how many 45 | bits you want after the cast: 46 | 47 | ```rust 48 | #fn main() { 49 | let x = bit_cast::<4, 6>(bits::<6>(5_u128)); // Same result - x is 5 represented with 4 bits. 50 | #} 51 | ``` 52 | 53 | -------------------------------------------------------------------------------- /guide/src/chapter_2.md: -------------------------------------------------------------------------------- 1 | # Chapter 2 - Signals 2 | 3 | The `Signal` class represents a connection pathway (think conveyor belt in our 4 | food factory analogy). It carries a value from one location on the FPGA to 5 | another. Furthermore, the `Signal` has two attributes that are part of its 6 | type signature: 7 | 8 | - Direction: This gives you a hint as to whether the signal is meant to carry 9 | data away from this part, or if the signal is meant to bring data into this part. 10 | - Kind: The second type argument for the signal tells you what kind of data the 11 | signal carries. 12 | 13 | So in the case of `Signal`, we know that the signal brings into the 14 | circuit a clock line (which is a special type used to represent clock signals). 15 | And in the case of `Signal>`, the signal carries 8 bits out from the 16 | circuit. That's basically it! Not much magic there. Let's look 17 | -------------------------------------------------------------------------------- /install_toolchain_notes.md: -------------------------------------------------------------------------------- 1 | On Fedora (36, x86-64), I needed the following to support ISE. 2 | 3 | ncurses-compat-libs 4 | 5 | There was also some symlinking nonsense with libharfbuzz to get Vivado to install. 6 | -------------------------------------------------------------------------------- /rust-hdl-bsp-alchitry-cu/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-hdl-bsp-alchitry-cu" 3 | version = "0.46.0" 4 | edition = "2021" 5 | license = "MIT" 6 | description = "Support crate for RustHDL - provides Board Support Package for the Alchitry Cu board" 7 | homepage = "https://github.com/samitbasu/rust-hdl" 8 | repository = "https://github.com/samitbasu/rust-hdl" 9 | keywords = ["fpga", "verilog", "hardware"] 10 | authors = ["Samit Basu "] 11 | 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [dependencies] 16 | rust-hdl = { version = "0.46.0", path = "../rust-hdl", features = ["fpga"] } 17 | -------------------------------------------------------------------------------- /rust-hdl-bsp-alchitry-cu/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod pins; 2 | pub mod synth; 3 | -------------------------------------------------------------------------------- /rust-hdl-bsp-alchitry-cu/src/pins.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl::core::prelude::*; 2 | 3 | pub const CLOCK_SPEED_100MHZ: u64 = 100_000_000; 4 | 5 | pub fn clock() -> Signal { 6 | let mut x = Signal::::default(); 7 | x.add_location(0, "P7"); 8 | x.connect(); 9 | x 10 | } 11 | 12 | pub fn leds() -> Signal> { 13 | let mut x = Signal::::default(); 14 | for (ndx, uname) in ["J11", "K11", "K12", "K14", "L12", "L14", "M12", "N14"] 15 | .iter() 16 | .enumerate() 17 | { 18 | x.add_location(ndx, uname); 19 | } 20 | x 21 | } 22 | 23 | pub fn map_alchitry_pin_to_cu_pad(pin: &str) -> &str { 24 | match pin { 25 | "A2" => "M1", 26 | 27 | "A3" => "L1", 28 | 29 | "A5" => "J1", 30 | 31 | "A6" => "J3", 32 | 33 | "A8" => "G1", 34 | 35 | "A9" => "G3", 36 | 37 | "A11" => "E1", 38 | 39 | "A12" => "D1", 40 | 41 | "A14" => "C1", 42 | 43 | "A15" => "B1", 44 | 45 | "A17" => "D3", 46 | 47 | "A18" => "C3", 48 | 49 | "A20" => "A1", 50 | 51 | "A21" => "A2", 52 | 53 | "A23" => "A3", 54 | 55 | "A24" => "A4", 56 | 57 | "A27" => "A5", 58 | 59 | "A28" => "C5", 60 | 61 | "A30" => "D5", 62 | 63 | "A31" => "C4", 64 | 65 | "A33" => "D4", 66 | 67 | "A34" => "E4", 68 | 69 | "A36" => "F4", 70 | 71 | "A37" => "F3", 72 | 73 | "A39" => "H4", 74 | 75 | "A40" => "G4", 76 | 77 | "A42" => "H1", 78 | 79 | "A43" => "H3", 80 | 81 | "A45" => "K3", 82 | 83 | "A46" => "K4", 84 | 85 | "A48" => "N1", 86 | 87 | "A49" => "P1", 88 | _ => { 89 | panic!("Unknown pin"); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /rust-hdl-bsp-alchitry-cu/tests/bsp_alchitry_cu_icepll.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl::fpga::lattice::ice40::ice_pll::ICE40PLLBlock; 2 | use rust_hdl::prelude::*; 3 | use rust_hdl_bsp_alchitry_cu::synth; 4 | 5 | #[test] 6 | fn test_pll_synthesizable() { 7 | const MHZ100: u64 = 100_000_000; 8 | const MHZ25: u64 = 25_000_000; 9 | let mut uut: ICE40PLLBlock = ICE40PLLBlock::default(); 10 | uut.clock_in.add_location(0, "P7"); 11 | uut.clock_in.connect(); 12 | uut.connect_all(); 13 | let vlog = generate_verilog(&uut); 14 | yosys_validate("vlog", &vlog).unwrap(); 15 | synth::generate_bitstream(uut, target_path!("alchitry_cu/pll_cu")); 16 | } 17 | -------------------------------------------------------------------------------- /rust-hdl-bsp-alchitry-cu/tests/bsp_alchitry_cu_pulser.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl::prelude::*; 2 | use rust_hdl_bsp_alchitry_cu::pins::CLOCK_SPEED_100MHZ; 3 | use rust_hdl_bsp_alchitry_cu::{pins, synth}; 4 | use std::time::Duration; 5 | 6 | pub const MHZ100: u64 = 100_000_000; 7 | 8 | #[derive(LogicBlock)] 9 | pub struct AlchitryCuPulser { 10 | pulser: Pulser, 11 | clock: Signal, 12 | leds: Signal>, 13 | } 14 | 15 | impl Logic for AlchitryCuPulser { 16 | #[hdl_gen] 17 | fn update(&mut self) { 18 | self.pulser.enable.next = true; 19 | clock!(self, clock, pulser); 20 | self.leds.next = 0x00.into(); 21 | if self.pulser.pulse.val() { 22 | self.leds.next = 0xAA.into(); 23 | } 24 | } 25 | } 26 | 27 | impl Default for AlchitryCuPulser { 28 | fn default() -> Self { 29 | let pulser = Pulser::new(CLOCK_SPEED_100MHZ.into(), 1.0, Duration::from_millis(250)); 30 | Self { 31 | pulser, 32 | clock: pins::clock(), 33 | leds: pins::leds(), 34 | } 35 | } 36 | } 37 | 38 | #[test] 39 | fn synthesize_alchitry_cu_pulser() { 40 | let uut = AlchitryCuPulser::default(); 41 | synth::generate_bitstream(uut, target_path!("alchitry_cu/pulser")); 42 | } 43 | -------------------------------------------------------------------------------- /rust-hdl-bsp-alchitry-cu/tests/bsp_alchitry_cu_pulser_pll.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl::fpga::lattice::ice40::ice_pll::ICE40PLLBlock; 2 | use rust_hdl::prelude::*; 3 | use rust_hdl_bsp_alchitry_cu::pins; 4 | use rust_hdl_bsp_alchitry_cu::synth::generate_bitstream; 5 | use std::time::Duration; 6 | 7 | const MHZ100: u64 = 100_000_000; 8 | const MHZ25: u64 = 25_000_000; 9 | 10 | #[derive(LogicBlock)] 11 | pub struct AlchitryCuPulserPLL { 12 | pulser: Pulser, 13 | clock: Signal, 14 | leds: Signal>, 15 | pll: ICE40PLLBlock, 16 | } 17 | 18 | impl Logic for AlchitryCuPulserPLL { 19 | #[hdl_gen] 20 | fn update(&mut self) { 21 | self.pll.clock_in.next = self.clock.val(); 22 | self.pulser.enable.next = self.pll.locked.val(); 23 | self.pulser.clock.next = self.pll.clock_out.val(); 24 | self.leds.next = 0x00.into(); 25 | if self.pulser.pulse.val() { 26 | self.leds.next = 0xAA.into(); 27 | } 28 | } 29 | } 30 | 31 | impl Default for AlchitryCuPulserPLL { 32 | fn default() -> Self { 33 | let pulser = Pulser::new(MHZ25, 1.0, Duration::from_millis(100)); 34 | Self { 35 | pulser, 36 | clock: pins::clock(), 37 | leds: pins::leds(), 38 | pll: ICE40PLLBlock::default(), 39 | } 40 | } 41 | } 42 | 43 | #[test] 44 | fn synthesize_alchitry_cu_pulser_with_pll() { 45 | let uut = AlchitryCuPulserPLL::default(); 46 | generate_bitstream(uut, target_path!("alchitry_cu/pulser_pll")); 47 | } 48 | -------------------------------------------------------------------------------- /rust-hdl-bsp-alchitry-cu/tests/bsp_alchitry_cu_pwm.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl::prelude::*; 2 | use rust_hdl_bsp_alchitry_cu::{pins, synth}; 3 | use std::collections::BTreeMap; 4 | 5 | #[derive(LogicBlock)] 6 | pub struct AlchitryCuPWM { 7 | pwm: PulseWidthModulator

, 8 | clock: Signal, 9 | strobe: Strobe<32>, 10 | leds: Signal>, 11 | rom: ROM, 8>, 12 | counter: DFF>, 13 | } 14 | 15 | impl Logic for AlchitryCuPWM

{ 16 | #[hdl_gen] 17 | fn update(&mut self) { 18 | clock!(self, clock, pwm, strobe, counter); 19 | self.pwm.enable.next = true; 20 | self.rom.address.next = self.counter.q.val(); 21 | self.pwm.threshold.next = self.rom.data.val(); 22 | self.strobe.enable.next = true; 23 | self.leds.next = 0x00.into(); 24 | if self.pwm.active.val() { 25 | self.leds.next = 0xFF.into(); 26 | } 27 | self.counter.d.next = self.counter.q.val() + self.strobe.strobe.val(); 28 | } 29 | } 30 | 31 | impl AlchitryCuPWM

{ 32 | fn new(clock_freq: u64) -> Self { 33 | let rom = (0..256) 34 | .map(|x| (x.to_bits(), snore(x))) 35 | .collect::>(); 36 | Self { 37 | pwm: PulseWidthModulator::default(), 38 | clock: pins::clock(), 39 | strobe: Strobe::new(clock_freq, 60.0), 40 | leds: pins::leds(), 41 | rom: ROM::new(rom), 42 | counter: Default::default(), 43 | } 44 | } 45 | } 46 | 47 | #[test] 48 | fn test_pwm_synthesizes() { 49 | let mut uut: AlchitryCuPWM<6> = AlchitryCuPWM::new(100_000_000); 50 | uut.connect_all(); 51 | let vlog = generate_verilog(&uut); 52 | yosys_validate("pwm_cu2", &vlog).unwrap(); 53 | synth::generate_bitstream(uut, target_path!("alchitry_cu/pwm_cu2")); 54 | } 55 | -------------------------------------------------------------------------------- /rust-hdl-bsp-alchitry-cu/tests/bsp_alchitry_cu_pwm_vec_srom.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl::fpga::lattice::ice40::ice_pll::ICE40PLLBlock; 2 | #[cfg(test)] 3 | use rust_hdl::prelude::*; 4 | use rust_hdl_bsp_alchitry_cu::{pins, synth}; 5 | 6 | const MHZ25: u64 = 25_000_000; 7 | const MHZ100: u64 = 100_000_000; 8 | 9 | #[derive(LogicBlock)] 10 | pub struct AlchitryCuPWMVecSyncROM { 11 | clock: Signal, 12 | leds: Signal>, 13 | local: Signal>, 14 | faders: [FaderWithSyncROM; 8], 15 | pll: ICE40PLLBlock, 16 | } 17 | 18 | impl Logic for AlchitryCuPWMVecSyncROM

{ 19 | #[hdl_gen] 20 | fn update(&mut self) { 21 | self.pll.clock_in.next = self.clock.val(); 22 | for i in 0..8 { 23 | self.faders[i].clock.next = self.pll.clock_out.val(); 24 | self.faders[i].enable.next = self.pll.locked.val(); 25 | } 26 | self.local.next = 0x00.into(); 27 | for i in 0..8 { 28 | self.local.next = self.local.val().replace_bit(i, self.faders[i].active.val()); 29 | } 30 | self.leds.next = self.local.val(); 31 | } 32 | } 33 | 34 | impl AlchitryCuPWMVecSyncROM

{ 35 | fn new(clock_frequency: u64) -> Self { 36 | let faders: [FaderWithSyncROM; 8] = [ 37 | FaderWithSyncROM::new(clock_frequency, 0), 38 | FaderWithSyncROM::new(clock_frequency, 18), 39 | FaderWithSyncROM::new(clock_frequency, 36), 40 | FaderWithSyncROM::new(clock_frequency, 54), 41 | FaderWithSyncROM::new(clock_frequency, 72), 42 | FaderWithSyncROM::new(clock_frequency, 90), 43 | FaderWithSyncROM::new(clock_frequency, 108), 44 | FaderWithSyncROM::new(clock_frequency, 128), 45 | ]; 46 | Self { 47 | clock: pins::clock(), 48 | leds: pins::leds(), 49 | local: Signal::default(), 50 | faders, 51 | pll: ICE40PLLBlock::default(), 52 | } 53 | } 54 | } 55 | 56 | #[test] 57 | fn test_pwm_vec_sync_rom_synthesizes() { 58 | let mut uut: AlchitryCuPWMVecSyncROM<6> = AlchitryCuPWMVecSyncROM::new(25_000_000); 59 | uut.connect_all(); 60 | let vlog = generate_verilog(&uut); 61 | yosys_validate("pwm_cu_srom", &vlog).unwrap(); 62 | synth::generate_bitstream(uut, target_path!("alchitry_cu/pwm_cu_srom")); 63 | } 64 | -------------------------------------------------------------------------------- /rust-hdl-bsp-ok-xem6010/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-hdl-bsp-ok-xem6010" 3 | version = "0.46.0" 4 | edition = "2021" 5 | license = "MIT" 6 | description = "Support crate for RustHDL - provides Board Support Package for the OpalKelly XEM6010 FPGA module (Spartan-6 based)" 7 | homepage = "https://rust-hdl.org" 8 | repository = "https://github.com/samitbasu/rust-hdl" 9 | keywords = ["fpga", "verilog", "hardware"] 10 | authors = ["Samit Basu "] 11 | 12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 13 | 14 | [dependencies] 15 | rust-hdl = { path = "../rust-hdl", version = "0.46.0", features = ["fpga"] } 16 | rust-hdl-ok-core = { path = "../rust-hdl-ok-core", version = "0.46.0" } 17 | rand = { version = "0.8.1" } 18 | 19 | [dev-dependencies] 20 | rust-hdl-hls = { path = "../rust-hdl-hls", version = "0.46.0" } 21 | rust-hdl-sim = { path = "../rust-hdl-sim", version = "0.46.0" } 22 | -------------------------------------------------------------------------------- /rust-hdl-bsp-ok-xem6010/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod xem6010; 2 | -------------------------------------------------------------------------------- /rust-hdl-bsp-ok-xem6010/src/xem6010/mod.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | 3 | use pins::*; 4 | use rust_hdl::prelude::*; 5 | use rust_hdl_ok_core::core::prelude::*; 6 | 7 | pub mod ddr_fifo; 8 | pub mod mcb_if; 9 | pub mod mig; 10 | pub mod ok_download_ddr; 11 | pub mod pins; 12 | pub mod pll; 13 | pub mod synth; 14 | 15 | #[derive(Clone, Debug)] 16 | pub struct XEM6010 {} 17 | 18 | impl OpalKellyBSP for XEM6010 { 19 | fn hi() -> OpalKellyHostInterface { 20 | OpalKellyHostInterface::xem_6010() 21 | } 22 | fn ok_host() -> OpalKellyHost { 23 | OpalKellyHost::xem_6010() 24 | } 25 | 26 | fn leds() -> Signal> { 27 | xem_6010_leds() 28 | } 29 | fn clocks() -> Vec> { 30 | vec![xem_6010_base_clock()] 31 | } 32 | 33 | fn synth(uut: U, dir: &str) { 34 | synth::synth_obj(uut, dir) 35 | } 36 | } 37 | 38 | #[derive(LogicBlock)] 39 | pub struct OKTest1 { 40 | pub hi: OpalKellyHostInterface, 41 | pub ok_host: OpalKellyHost, 42 | pub led: Signal>, 43 | pub pulser: Pulser, 44 | pub auto_reset: AutoReset, 45 | } 46 | 47 | impl OKTest1 { 48 | pub fn new() -> Self { 49 | Self { 50 | hi: OpalKellyHostInterface::xem_6010(), 51 | ok_host: OpalKellyHost::xem_6010(), 52 | led: pins::xem_6010_leds(), 53 | pulser: Pulser::new(MHZ48, 1.0, Duration::from_millis(500)), 54 | auto_reset: Default::default(), 55 | } 56 | } 57 | } 58 | 59 | impl Logic for OKTest1 { 60 | #[hdl_gen] 61 | fn update(&mut self) { 62 | OpalKellyHostInterface::link(&mut self.hi, &mut self.ok_host.hi); 63 | self.auto_reset.clock.next = self.ok_host.ti_clk.val(); 64 | self.pulser.clock.next = self.ok_host.ti_clk.val(); 65 | self.pulser.enable.next = true; 66 | if self.pulser.pulse.val() { 67 | self.led.next = 0xFF.into(); 68 | } else { 69 | self.led.next = 0x00.into(); 70 | } 71 | } 72 | } 73 | 74 | #[test] 75 | fn test_ok_host_synthesizable() { 76 | let mut uut = OKTest1::new(); 77 | uut.connect_all(); 78 | let vlog = generate_verilog(&uut); 79 | let _ucf = rust_hdl::fpga::toolchains::ise::generate_ucf(&uut); 80 | yosys_validate("vlog", &vlog).unwrap(); 81 | } 82 | -------------------------------------------------------------------------------- /rust-hdl-bsp-ok-xem6010/src/xem6010/pins.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl::prelude::*; 2 | 3 | pub fn xem_6010_leds() -> Signal> { 4 | let mut x = Signal::default(); 5 | for (ndx, name) in [ 6 | "Y17", "AB17", "AA14", "AB14", "AA16", "AB16", "AA10", "AB10", 7 | ] 8 | .iter() 9 | .enumerate() 10 | { 11 | x.add_location(ndx, name); 12 | x.add_signal_type(ndx, SignalType::LowVoltageCMOS_3v3); 13 | } 14 | x 15 | } 16 | 17 | pub fn xem_6010_base_clock() -> Signal { 18 | let mut x = Signal::default(); 19 | x.add_location(0, "AB13"); 20 | x.add_signal_type(0, SignalType::LowVoltageCMOS_3v3); 21 | x.add_constraint(PinConstraint { 22 | index: 0, 23 | constraint: Constraint::Timing(Timing::Periodic(PeriodicTiming { 24 | net: "SystemClk".into(), 25 | period_nanoseconds: 10.0, 26 | duty_cycle: 50.0, 27 | })), 28 | }); 29 | x 30 | } 31 | -------------------------------------------------------------------------------- /rust-hdl-bsp-ok-xem6010/tests/bsp_ok_xem6010_blinky.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl_ok_core::core::prelude::*; 2 | 3 | use rust_hdl::prelude::*; 4 | use rust_hdl_bsp_ok_xem6010::xem6010::XEM6010; 5 | use rust_hdl_ok_core::test_common::blinky::OpalKellyBlinky; 6 | 7 | #[test] 8 | fn test_opalkelly_xem_6010_synth_blinky() { 9 | let mut uut = OpalKellyBlinky::new::(); 10 | uut.hi.link_connect_dest(); 11 | uut.connect_all(); 12 | XEM6010::synth(uut, target_path!("xem_6010/blinky")); 13 | } 14 | -------------------------------------------------------------------------------- /rust-hdl-bsp-ok-xem6010/tests/bsp_ok_xem6010_download.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl::prelude::*; 2 | use rust_hdl_bsp_ok_xem6010::xem6010; 3 | use rust_hdl_bsp_ok_xem6010::xem6010::XEM6010; 4 | use rust_hdl_ok_core::test_common::download::{ 5 | test_opalkelly_download_runtime, OpalKellyDownloadFIFOTest, 6 | }; 7 | 8 | #[test] 9 | fn test_opalkelly_xem_6010_synth_download() { 10 | let mut uut = OpalKellyDownloadFIFOTest::new::(); 11 | uut.hi.link_connect_dest(); 12 | uut.connect_all(); 13 | xem6010::synth::synth_obj(uut, target_path!("xem_6010/download")); 14 | test_opalkelly_download_runtime( 15 | target_path!("xem_6010/download/top.bit"), 16 | env!("XEM6010_SERIAL"), 17 | ) 18 | .unwrap() 19 | } 20 | -------------------------------------------------------------------------------- /rust-hdl-bsp-ok-xem6010/tests/bsp_ok_xem6010_download32.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl::prelude::*; 2 | use rust_hdl_bsp_ok_xem6010::xem6010::{synth, XEM6010}; 3 | use rust_hdl_ok_core::test_common::download::{ 4 | test_opalkelly_download32_runtime, OpalKellyDownload32FIFOTest, 5 | }; 6 | 7 | #[test] 8 | fn test_opalkelly_xem_6010_synth_download32() { 9 | let mut uut = OpalKellyDownload32FIFOTest::new::(); 10 | uut.hi.link_connect_dest(); 11 | uut.connect_all(); 12 | synth::synth_obj(uut, target_path!("xem_6010/download32")); 13 | test_opalkelly_download32_runtime( 14 | target_path!("xem_6010/download32/top.bit"), 15 | env!("XEM6010_SERIAL"), 16 | ) 17 | .unwrap(); 18 | } 19 | -------------------------------------------------------------------------------- /rust-hdl-bsp-ok-xem6010/tests/bsp_ok_xem6010_fir.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl_ok_core::core::prelude::*; 2 | 3 | use rust_hdl::prelude::*; 4 | use rust_hdl_bsp_ok_xem6010::xem6010::XEM6010; 5 | use rust_hdl_ok_core::test_common::fir::{test_opalkelly_fir_runtime, OpalKellyFIRTest}; 6 | 7 | #[test] 8 | fn test_opalkelly_xem_6010_fir() { 9 | let mut uut = OpalKellyFIRTest::new::(); 10 | uut.hi.link_connect_dest(); 11 | uut.connect_all(); 12 | XEM6010::synth(uut, target_path!("xem_6010/fir")); 13 | test_opalkelly_fir_runtime(target_path!("xem_6010/fir/top.bit"), env!("XEM6010_SERIAL")) 14 | .unwrap(); 15 | } 16 | -------------------------------------------------------------------------------- /rust-hdl-bsp-ok-xem6010/tests/bsp_ok_xem6010_mux_spi.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl::prelude::*; 2 | use rust_hdl_bsp_ok_xem6010::xem6010; 3 | use rust_hdl_bsp_ok_xem6010::xem6010::XEM6010; 4 | use rust_hdl_ok_core::test_common::mux_spi::{test_opalkelly_mux_spi_runtime, OpalKellySPIMuxTest}; 5 | 6 | #[test] 7 | fn test_opalkelly_xem_6010_mux_spi() { 8 | let mut uut = OpalKellySPIMuxTest::new::(); 9 | uut.hi.link_connect_dest(); 10 | uut.connect_all(); 11 | xem6010::synth::synth_obj(uut, target_path!("xem_6010/mux_spi")); 12 | test_opalkelly_mux_spi_runtime( 13 | target_path!("xem_6010/mux_spi/top.bit"), 14 | env!("XEM6010_SERIAL"), 15 | ) 16 | .unwrap() 17 | } 18 | -------------------------------------------------------------------------------- /rust-hdl-bsp-ok-xem6010/tests/bsp_ok_xem6010_pipe_fifo.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl_ok_core::core::prelude::*; 2 | 3 | use rust_hdl::prelude::*; 4 | use rust_hdl_bsp_ok_xem6010::xem6010::XEM6010; 5 | use rust_hdl_ok_core::test_common::pipe::{ 6 | test_opalkelly_pipe_fifo_runtime, OpalKellyPipeFIFOTest, 7 | }; 8 | 9 | #[test] 10 | fn test_opalkelly_xem_6010_synth_pipe_fifo() { 11 | let mut uut = OpalKellyPipeFIFOTest::new::(); 12 | uut.connect_all(); 13 | XEM6010::synth(uut, target_path!("xem_6010/pipe_fifo")); 14 | test_opalkelly_pipe_fifo_runtime( 15 | target_path!("xem_6010/pipe_fifo/top.bit"), 16 | env!("XEM6010_SERIAL"), 17 | ) 18 | .unwrap(); 19 | } 20 | -------------------------------------------------------------------------------- /rust-hdl-bsp-ok-xem6010/tests/bsp_ok_xem6010_pipe_in.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl::prelude::*; 2 | use rust_hdl_bsp_ok_xem6010::xem6010::XEM6010; 3 | use rust_hdl_ok_core::core::prelude::*; 4 | use rust_hdl_ok_core::test_common::pipe::{test_opalkelly_pipe_in_runtime, OpalKellyPipeTest}; 5 | 6 | #[test] 7 | fn test_opalkelly_xem_6010_synth_pipe() { 8 | let mut uut = OpalKellyPipeTest::new::(); 9 | uut.connect_all(); 10 | XEM6010::synth(uut, target_path!("xem_6010/pipe_in")); 11 | test_opalkelly_pipe_in_runtime( 12 | target_path!("xem_6010/pipe_in/top.bit"), 13 | env!("XEM6010_SERIAL"), 14 | ) 15 | .unwrap(); 16 | } 17 | -------------------------------------------------------------------------------- /rust-hdl-bsp-ok-xem6010/tests/bsp_ok_xem6010_pipe_ram.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl::prelude::*; 2 | use rust_hdl_bsp_ok_xem6010::xem6010::XEM6010; 3 | use rust_hdl_ok_core::core::prelude::OpalKellyBSP; 4 | use rust_hdl_ok_core::test_common::pipe::{test_opalkelly_pipe_ram_runtime, OpalKellyPipeRAMTest}; 5 | 6 | #[test] 7 | fn test_opalkelly_xem_6010_synth_pipe_ram() { 8 | let mut uut = OpalKellyPipeRAMTest::new::(); 9 | uut.connect_all(); 10 | XEM6010::synth(uut, target_path!("xem_6010/pipe_ram")); 11 | test_opalkelly_pipe_ram_runtime( 12 | target_path!("xem_6010/pipe_ram/top.bit"), 13 | env!("XEM6010_SERIAL"), 14 | ) 15 | .unwrap(); 16 | } 17 | -------------------------------------------------------------------------------- /rust-hdl-bsp-ok-xem6010/tests/bsp_ok_xem6010_soc_test.rs: -------------------------------------------------------------------------------- 1 | // Build a SoC and connect it to an OK host 2 | 3 | use rust_hdl::prelude::*; 4 | use rust_hdl_bsp_ok_xem6010::xem6010::pins::xem_6010_base_clock; 5 | use rust_hdl_bsp_ok_xem6010::xem6010::XEM6010; 6 | use rust_hdl_ok_core::core::prelude::*; 7 | use rust_hdl_ok_core::test_common::soc::test_opalkelly_soc_hello; 8 | 9 | #[derive(LogicBlock)] 10 | struct OpalKellySoCTest { 11 | hi: OpalKellyHostInterface, 12 | ok_host: OpalKellyHost, 13 | sys_clock: Signal, 14 | pipe_in: PipeIn, 15 | pipe_out: PipeOut, 16 | dut: SoCTestChip, 17 | read_delay: DFF, 18 | } 19 | 20 | impl Default for OpalKellySoCTest { 21 | fn default() -> Self { 22 | Self { 23 | hi: XEM6010::hi(), 24 | ok_host: XEM6010::ok_host(), 25 | sys_clock: xem_6010_base_clock(), 26 | pipe_in: PipeIn::new(0x80), 27 | pipe_out: PipeOut::new(0xA0), 28 | dut: Default::default(), 29 | read_delay: Default::default(), 30 | } 31 | } 32 | } 33 | 34 | impl Logic for OpalKellySoCTest { 35 | #[hdl_gen] 36 | fn update(&mut self) { 37 | OpalKellyHostInterface::link(&mut self.hi, &mut self.ok_host.hi); 38 | self.read_delay.clock.next = self.ok_host.ti_clk.val(); 39 | self.dut.clock.next = self.ok_host.ti_clk.val(); 40 | self.dut.sys_clock.next = self.sys_clock.val(); 41 | self.dut.from_cpu.data.next = self.pipe_in.dataout.val(); 42 | self.dut.from_cpu.write.next = self.pipe_in.write.val(); 43 | self.pipe_out.datain.next = self.dut.to_cpu.data.val(); 44 | self.read_delay.d.next = self.pipe_out.read.val(); 45 | self.dut.to_cpu.read.next = self.read_delay.q.val(); 46 | self.pipe_in.ok1.next = self.ok_host.ok1.val(); 47 | self.pipe_out.ok1.next = self.ok_host.ok1.val(); 48 | self.ok_host.ok2.next = self.pipe_in.ok2.val() | self.pipe_out.ok2.val(); 49 | } 50 | } 51 | 52 | #[test] 53 | fn test_opalkelly_xem_6010_soc() { 54 | let mut uut = OpalKellySoCTest::default(); 55 | uut.hi.link_connect_dest(); 56 | uut.sys_clock.connect(); 57 | uut.connect_all(); 58 | XEM6010::synth(uut, target_path!("xem_6010/soc_hello")); 59 | test_opalkelly_soc_hello( 60 | target_path!("xem_6010/soc_hello/top.bit"), 61 | env!("XEM6010_SERIAL"), 62 | ) 63 | .unwrap(); 64 | } 65 | -------------------------------------------------------------------------------- /rust-hdl-bsp-ok-xem6010/tests/bsp_ok_xem6010_spi.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl::prelude::*; 2 | use rust_hdl_bsp_ok_xem6010::xem6010::{synth, XEM6010}; 3 | use rust_hdl_ok_core::test_common::spi::{ 4 | test_opalkelly_spi_reg_read_runtime, test_opalkelly_spi_reg_write_runtime, 5 | test_opalkelly_spi_single_conversion_runtime, OpalKellySPITest, 6 | }; 7 | 8 | #[test] 9 | fn test_opalkelly_xem_6010_synth_spi() { 10 | let mut uut = OpalKellySPITest::new::(); 11 | uut.connect_all(); 12 | synth::synth_obj(uut, target_path!("xem_6010/spi")); 13 | test_opalkelly_spi_reg_read_runtime( 14 | target_path!("xem_6010/spi/top.bit"), 15 | env!("XEM6010_SERIAL"), 16 | ) 17 | .unwrap(); 18 | test_opalkelly_spi_reg_write_runtime( 19 | target_path!("xem_6010/spi/top.bit"), 20 | env!("XEM6010_SERIAL"), 21 | ) 22 | .unwrap(); 23 | test_opalkelly_spi_single_conversion_runtime( 24 | target_path!("xem_6010/spi/top.bit"), 25 | env!("XEM6010_SERIAL"), 26 | ) 27 | .unwrap(); 28 | } 29 | -------------------------------------------------------------------------------- /rust-hdl-bsp-ok-xem6010/tests/bsp_ok_xem6010_tristate.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl_ok_core::core::ok_hi::OpalKellyHostInterface; 2 | use rust_hdl_ok_core::core::prelude::*; 3 | 4 | use rust_hdl::prelude::*; 5 | use rust_hdl_bsp_ok_xem6010::xem6010::XEM6010; 6 | 7 | #[derive(LogicBlock)] 8 | pub struct OpalKellyTristateBuffer { 9 | pub bus_pin: Signal, 10 | pub buffer: TristateBuffer, 11 | pub hi: OpalKellyHostInterface, 12 | pub ok_host: OpalKellyHost, 13 | pub control: WireIn, 14 | pub readout: WireOut, 15 | } 16 | 17 | impl Logic for OpalKellyTristateBuffer { 18 | #[hdl_gen] 19 | fn update(&mut self) { 20 | Signal::::link(&mut self.bus_pin, &mut self.buffer.bus); 21 | OpalKellyHostInterface::link(&mut self.hi, &mut self.ok_host.hi); 22 | self.control.ok1.next = self.ok_host.ok1.val(); 23 | self.readout.ok1.next = self.ok_host.ok1.val(); 24 | self.ok_host.ok2.next = self.readout.ok2.val(); 25 | self.buffer.write_enable.next = self.control.dataout.val().get_bit(0); 26 | self.buffer.write_data.next = self.control.dataout.val().get_bit(1); 27 | self.readout.datain.next = bit_cast::<16, 1>(self.buffer.read_data.val().into()); 28 | } 29 | } 30 | 31 | impl Default for OpalKellyTristateBuffer { 32 | fn default() -> Self { 33 | let mut x = Signal::default(); 34 | x.add_location(0, "K20"); 35 | x.add_signal_type(0, SignalType::LowVoltageCMOS_3v3); 36 | Self { 37 | bus_pin: x, 38 | buffer: Default::default(), 39 | hi: XEM6010::hi(), 40 | ok_host: XEM6010::ok_host(), 41 | control: WireIn::new(0), 42 | readout: WireOut::new(0x20), 43 | } 44 | } 45 | } 46 | 47 | #[test] 48 | fn test_opalkelly_xem_6010_synth_tribuffer() { 49 | let mut uut = OpalKellyTristateBuffer::default(); 50 | uut.hi.link_connect_dest(); 51 | uut.connect_all(); 52 | XEM6010::synth(uut, target_path!("xem_6010/tristate")); 53 | } 54 | -------------------------------------------------------------------------------- /rust-hdl-bsp-ok-xem6010/tests/bsp_ok_xem6010_wave.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl::prelude::*; 2 | use rust_hdl_bsp_ok_xem6010::xem6010::*; 3 | use rust_hdl_ok_core::core::bsp::OpalKellyBSP; 4 | use rust_hdl_ok_core::test_common::wave::OpalKellyWave; 5 | 6 | #[test] 7 | fn test_opalkelly_xem_6010_synth_wave() { 8 | let mut uut = OpalKellyWave::new::(); 9 | uut.connect_all(); 10 | XEM6010::synth(uut, target_path!("xem_6010/wave")); 11 | } 12 | -------------------------------------------------------------------------------- /rust-hdl-bsp-ok-xem6010/tests/bsp_ok_xem6010_wire.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl::prelude::*; 2 | use rust_hdl_bsp_ok_xem6010::xem6010::{synth, XEM6010}; 3 | use rust_hdl_ok_core::test_common::wire::{test_opalkelly_xem_wire_runtime, OpalKellyWireTest}; 4 | 5 | #[test] 6 | fn test_opalkelly_xem_6010_synth_wire() { 7 | let mut uut = OpalKellyWireTest::new::(); 8 | uut.connect_all(); 9 | synth::synth_obj(uut, target_path!("xem_6010/wire")); 10 | test_opalkelly_xem_wire_runtime( 11 | target_path!("xem_6010/wire/top.bit"), 12 | env!("XEM6010_SERIAL"), 13 | ) 14 | .unwrap() 15 | } 16 | -------------------------------------------------------------------------------- /rust-hdl-bsp-ok-xem7010/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-hdl-bsp-ok-xem7010" 3 | version = "0.46.0" 4 | edition = "2021" 5 | license = "MIT" 6 | description = "Support crate for RustHDL - provides Board Support Package for the OpalKelly XEM7010 module (Artix-7 based)" 7 | homepage = "https://rust-hdl.org" 8 | repository = "https://github.com/samitbasu/rust-hdl" 9 | keywords = ["fpga", "verilog", "hardware"] 10 | authors = ["Samit Basu "] 11 | 12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 13 | 14 | [dependencies] 15 | rust-hdl = { path = "../rust-hdl", version = "0.46.0", features = ["fpga"] } 16 | rust-hdl-ok-core = { path = "../rust-hdl-ok-core", version = "0.46.0" } 17 | rust-hdl-ok-frontpanel-sys = { path = "../rust-hdl-ok-frontpanel-sys", version = "0.46.0" } 18 | rand = { version = "0.8.1" } 19 | -------------------------------------------------------------------------------- /rust-hdl-bsp-ok-xem7010/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod xem7010; 2 | -------------------------------------------------------------------------------- /rust-hdl-bsp-ok-xem7010/src/xem7010/mod.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl::prelude::*; 2 | 3 | pub mod ddr_fifo7; 4 | pub mod download; 5 | pub mod mcb_if; 6 | pub mod mig7; 7 | pub mod pins; 8 | pub mod synth; 9 | pub mod sys_clock; 10 | 11 | use pins::*; 12 | use rust_hdl_ok_core::core::prelude::*; 13 | 14 | #[derive(Clone, Debug)] 15 | pub struct XEM7010 {} 16 | 17 | impl OpalKellyBSP for XEM7010 { 18 | fn hi() -> OpalKellyHostInterface { 19 | OpalKellyHostInterface::xem_7010() 20 | } 21 | fn ok_host() -> OpalKellyHost { 22 | OpalKellyHost::xem_7010() 23 | } 24 | 25 | fn leds() -> Signal> { 26 | xem_7010_leds() 27 | } 28 | fn clocks() -> Vec> { 29 | vec![xem_7010_pos_clock(), xem_7010_neg_clock()] 30 | } 31 | 32 | fn synth(uut: U, dir: &str) { 33 | synth::synth_obj(uut, dir) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /rust-hdl-bsp-ok-xem7010/src/xem7010/pins.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl::prelude::*; 2 | 3 | pub fn xem_7010_leds() -> Signal> { 4 | let mut x = Signal::default(); 5 | for (ndx, name) in ["N13", "N14", "P15", "P16", "N17", "P17", "R16", "R17"] 6 | .iter() 7 | .enumerate() 8 | { 9 | x.add_location(ndx, name); 10 | x.add_signal_type(ndx, SignalType::LowVoltageCMOS_3v3); 11 | } 12 | x 13 | } 14 | 15 | pub fn xem_7010_pos_clock() -> Signal { 16 | let mut x = Signal::default(); 17 | x.add_location(0, "K4"); 18 | x.add_signal_type(0, SignalType::LowVoltageDifferentialSignal_2v5); 19 | x.connect(); 20 | x.add_constraint(PinConstraint { 21 | index: 0, 22 | constraint: Constraint::Timing(Timing::Periodic(PeriodicTiming { 23 | net: "SystemClk".into(), 24 | period_nanoseconds: 5.0, 25 | duty_cycle: 50.0, 26 | })), 27 | }); 28 | x 29 | } 30 | 31 | pub fn xem_7010_neg_clock() -> Signal { 32 | let mut x = Signal::default(); 33 | x.add_location(0, "J4"); 34 | x.add_signal_type(0, SignalType::LowVoltageDifferentialSignal_2v5); 35 | x.connect(); 36 | x 37 | } 38 | -------------------------------------------------------------------------------- /rust-hdl-bsp-ok-xem7010/tests/bsp_ok_xem7010_blinky.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl::prelude::*; 2 | use rust_hdl_bsp_ok_xem7010::xem7010::XEM7010; 3 | use rust_hdl_ok_core::core::prelude::*; 4 | use rust_hdl_ok_core::test_common::blinky::OpalKellyBlinky; 5 | 6 | #[test] 7 | fn test_opalkelly_xem_7010_synth_blinky() { 8 | let mut uut = OpalKellyBlinky::new::(); 9 | uut.hi.link_connect_dest(); 10 | uut.connect_all(); 11 | XEM7010::synth(uut, target_path!("xem_7010/blinky")); 12 | } 13 | -------------------------------------------------------------------------------- /rust-hdl-bsp-ok-xem7010/tests/bsp_ok_xem7010_download.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl::prelude::*; 2 | use rust_hdl_bsp_ok_xem7010::xem7010::XEM7010; 3 | use rust_hdl_ok_core::core::prelude::*; 4 | use rust_hdl_ok_core::test_common::download::{ 5 | test_opalkelly_download_runtime, OpalKellyDownloadFIFOTest, 6 | }; 7 | 8 | #[test] 9 | fn test_opalkelly_xem_7010_synth_download() { 10 | let mut uut = OpalKellyDownloadFIFOTest::new::(); 11 | uut.hi.link_connect_dest(); 12 | uut.connect_all(); 13 | XEM7010::synth(uut, target_path!("xem_7010/download")); 14 | test_opalkelly_download_runtime( 15 | target_path!("xem_7010/download/top.bit"), 16 | env!("XEM7010_SERIAL"), 17 | ) 18 | .unwrap(); 19 | } 20 | -------------------------------------------------------------------------------- /rust-hdl-bsp-ok-xem7010/tests/bsp_ok_xem7010_download32.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl::prelude::*; 2 | use rust_hdl_bsp_ok_xem7010::xem7010::XEM7010; 3 | use rust_hdl_ok_core::core::prelude::*; 4 | use rust_hdl_ok_core::test_common::download::{ 5 | test_opalkelly_download32_runtime, OpalKellyDownload32FIFOTest, 6 | }; 7 | 8 | #[test] 9 | fn test_opalkelly_xem_7010_synth_download32() { 10 | let mut uut = OpalKellyDownload32FIFOTest::new::(); 11 | uut.hi.link_connect_dest(); 12 | uut.connect_all(); 13 | XEM7010::synth(uut, target_path!("xem_7010/download32")); 14 | test_opalkelly_download32_runtime( 15 | target_path!("xem_7010/download32/top.bit"), 16 | env!("XEM7010_SERIAL"), 17 | ) 18 | .unwrap(); 19 | } 20 | -------------------------------------------------------------------------------- /rust-hdl-bsp-ok-xem7010/tests/bsp_ok_xem7010_fast_blinky.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl::prelude::*; 2 | use rust_hdl_bsp_ok_xem7010::xem7010::sys_clock::OpalKellySystemClock7; 3 | use rust_hdl_bsp_ok_xem7010::xem7010::XEM7010; 4 | use rust_hdl_ok_core::core::prelude::*; 5 | use std::time::Duration; 6 | 7 | #[derive(LogicBlock)] 8 | pub struct OpalKellyFastBlinky { 9 | pub led: Signal>, 10 | pub pulser: Pulser, 11 | pub clock_p: Signal, 12 | pub clock_n: Signal, 13 | pub clk_100mhz: Signal, 14 | pub clock_div: OpalKellySystemClock7, 15 | } 16 | 17 | impl OpalKellyFastBlinky { 18 | pub fn new() -> Self { 19 | let clk = B::clocks(); 20 | Self { 21 | led: B::leds(), 22 | pulser: Pulser::new(MHZ100, 1.0, Duration::from_millis(500)), 23 | clock_p: clk[0].clone(), 24 | clock_n: clk[1].clone(), 25 | clk_100mhz: Default::default(), 26 | clock_div: Default::default(), 27 | } 28 | } 29 | } 30 | 31 | impl Logic for OpalKellyFastBlinky { 32 | #[hdl_gen] 33 | fn update(&mut self) { 34 | self.clock_div.clock_p.next = self.clock_p.val(); 35 | self.clock_div.clock_n.next = self.clock_n.val(); 36 | self.clk_100mhz.next = self.clock_div.sys_clock.val(); 37 | clock!(self, clk_100mhz, pulser); 38 | self.pulser.enable.next = true; 39 | if self.pulser.pulse.val() { 40 | self.led.next = 0xFF.into(); 41 | } else { 42 | self.led.next = 0x00.into(); 43 | } 44 | } 45 | } 46 | 47 | #[test] 48 | fn test_fast_blinky_is_synthesizable() { 49 | let mut uut = OpalKellyFastBlinky::new::(); 50 | uut.clock_n.connect(); 51 | uut.clock_p.connect(); 52 | uut.connect_all(); 53 | yosys_validate("fast_blinky_7010", &generate_verilog(&uut)).unwrap(); 54 | } 55 | 56 | #[test] 57 | fn test_opalkelly_xem_7010_synth_fast_blinky() { 58 | let mut uut = OpalKellyFastBlinky::new::(); 59 | uut.clock_n.connect(); 60 | uut.clock_p.connect(); 61 | uut.connect_all(); 62 | XEM7010::synth(uut, target_path!("xem_7010/fast_blinky")); 63 | } 64 | -------------------------------------------------------------------------------- /rust-hdl-bsp-ok-xem7010/tests/bsp_ok_xem7010_mux_spi.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl::prelude::*; 2 | use rust_hdl_bsp_ok_xem7010::xem7010::XEM7010; 3 | use rust_hdl_ok_core::core::prelude::*; 4 | use rust_hdl_ok_core::test_common::mux_spi::{test_opalkelly_mux_spi_runtime, OpalKellySPIMuxTest}; 5 | 6 | #[test] 7 | fn test_opalkelly_xem_7010_mux_spi() { 8 | let mut uut = OpalKellySPIMuxTest::new::(); 9 | uut.hi.link_connect_dest(); 10 | uut.connect_all(); 11 | XEM7010::synth(uut, target_path!("xem_7010/mux_spi")); 12 | test_opalkelly_mux_spi_runtime( 13 | target_path!("xem_7010/mux_spi/top.bit"), 14 | env!("XEM7010_SERIAL"), 15 | ) 16 | .unwrap() 17 | } 18 | -------------------------------------------------------------------------------- /rust-hdl-bsp-ok-xem7010/tests/bsp_ok_xem7010_pipe_fifo.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl::prelude::*; 2 | use rust_hdl_bsp_ok_xem7010::xem7010::XEM7010; 3 | use rust_hdl_ok_core::{ 4 | core::prelude::OpalKellyBSP, 5 | test_common::pipe::{test_opalkelly_pipe_fifo_runtime, OpalKellyPipeFIFOTest}, 6 | }; 7 | 8 | #[test] 9 | fn test_opalkelly_xem_7010_synth_pipe_fifo() { 10 | let mut uut = OpalKellyPipeFIFOTest::new::(); 11 | uut.hi.sig_inout.connect(); 12 | uut.hi.sig_in.connect(); 13 | uut.hi.sig_out.connect(); 14 | uut.hi.sig_aa.connect(); 15 | uut.connect_all(); 16 | XEM7010::synth(uut, target_path!("xem_7010/pipe_fifo")); 17 | test_opalkelly_pipe_fifo_runtime( 18 | target_path!("xem_7010/pipe_fifo/top.bit"), 19 | env!("XEM7010_SERIAL"), 20 | ) 21 | .unwrap(); 22 | } 23 | -------------------------------------------------------------------------------- /rust-hdl-bsp-ok-xem7010/tests/bsp_ok_xem7010_pipe_in.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl::prelude::*; 2 | use rust_hdl_bsp_ok_xem7010::xem7010::XEM7010; 3 | use rust_hdl_ok_core::core::prelude::*; 4 | use rust_hdl_ok_core::test_common::pipe::{test_opalkelly_pipe_in_runtime, OpalKellyPipeTest}; 5 | 6 | #[test] 7 | fn test_opalkelly_xem_7010_synthesizes() { 8 | let mut uut = OpalKellyPipeTest::new::(); 9 | uut.hi.link_connect_dest(); 10 | uut.connect_all(); 11 | yosys_validate("pipe_in_7010", &generate_verilog(&uut)).unwrap(); 12 | } 13 | 14 | #[test] 15 | fn test_opalkelly_xem_7010_synth_pipe() { 16 | let mut uut = OpalKellyPipeTest::new::(); 17 | uut.hi.link_connect_dest(); 18 | uut.connect_all(); 19 | XEM7010::synth(uut, target_path!("xem_7010/pipe_in")); 20 | test_opalkelly_pipe_in_runtime( 21 | target_path!("xem_7010/pipe_in/top.bit"), 22 | env!("XEM7010_SERIAL"), 23 | ) 24 | .unwrap(); 25 | } 26 | -------------------------------------------------------------------------------- /rust-hdl-bsp-ok-xem7010/tests/bsp_ok_xem7010_pipe_ram.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl::prelude::*; 2 | use rust_hdl_bsp_ok_xem7010::xem7010::XEM7010; 3 | use rust_hdl_ok_core::core::prelude::*; 4 | use rust_hdl_ok_core::test_common::pipe::{test_opalkelly_pipe_ram_runtime, OpalKellyPipeRAMTest}; 5 | 6 | #[test] 7 | fn test_opalkelly_xem_7010_synth_pipe_ram() { 8 | let mut uut = OpalKellyPipeRAMTest::new::(); 9 | uut.hi.link_connect_dest(); 10 | uut.connect_all(); 11 | XEM7010::synth(uut, target_path!("xem_7010/pipe_ram")); 12 | test_opalkelly_pipe_ram_runtime( 13 | target_path!("xem_7010/pipe_ram/top.bit"), 14 | env!("XEM7010_SERIAL"), 15 | ) 16 | .unwrap(); 17 | } 18 | -------------------------------------------------------------------------------- /rust-hdl-bsp-ok-xem7010/tests/bsp_ok_xem7010_spi.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl::prelude::*; 2 | use rust_hdl_bsp_ok_xem7010::xem7010::XEM7010; 3 | use rust_hdl_ok_core::core::bsp::OpalKellyBSP; 4 | use rust_hdl_ok_core::test_common::spi::{ 5 | test_opalkelly_spi_reg_read_runtime, test_opalkelly_spi_reg_write_runtime, 6 | test_opalkelly_spi_single_conversion_runtime, OpalKellySPITest, 7 | }; 8 | 9 | #[test] 10 | fn test_opalkelly_xem_7010_synth_spi() { 11 | let mut uut = OpalKellySPITest::new::(); 12 | uut.hi.link_connect_dest(); 13 | uut.connect_all(); 14 | XEM7010::synth(uut, target_path!("xem_7010/spi")); 15 | test_opalkelly_spi_reg_read_runtime( 16 | target_path!("xem_7010/spi/top.bit"), 17 | env!("XEM7010_SERIAL"), 18 | ) 19 | .unwrap(); 20 | test_opalkelly_spi_reg_write_runtime( 21 | target_path!("xem_7010/spi/top.bit"), 22 | env!("XEM7010_SERIAL"), 23 | ) 24 | .unwrap(); 25 | test_opalkelly_spi_single_conversion_runtime( 26 | target_path!("xem_7010/spi/top.bit"), 27 | env!("XEM7010_SERIAL"), 28 | ) 29 | .unwrap(); 30 | } 31 | -------------------------------------------------------------------------------- /rust-hdl-bsp-ok-xem7010/tests/bsp_ok_xem7010_wave.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl::prelude::*; 2 | use rust_hdl_bsp_ok_xem7010::xem7010::XEM7010; 3 | use rust_hdl_ok_core::core::prelude::*; 4 | use rust_hdl_ok_core::test_common::wave::OpalKellyWave; 5 | 6 | #[test] 7 | fn test_opalkelly_xem_7010_synth_wave() { 8 | let mut uut = OpalKellyWave::new::(); 9 | uut.hi.sig_in.connect(); 10 | uut.hi.sig_out.connect(); 11 | uut.hi.sig_inout.connect(); 12 | uut.hi.sig_aa.connect(); 13 | uut.connect_all(); 14 | XEM7010::synth(uut, target_path!("xem_7010/wave")); 15 | } 16 | -------------------------------------------------------------------------------- /rust-hdl-bsp-ok-xem7010/tests/bsp_ok_xem7010_wire.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl::prelude::*; 2 | use rust_hdl_bsp_ok_xem7010::xem7010::XEM7010; 3 | use rust_hdl_ok_core::core::prelude::*; 4 | use rust_hdl_ok_core::test_common::wire::{test_opalkelly_xem_wire_runtime, OpalKellyWireTest}; 5 | 6 | #[test] 7 | fn test_opalkelly_xem_7010_synth_wire() { 8 | let mut uut = OpalKellyWireTest::new::(); 9 | uut.hi.link_connect_dest(); 10 | uut.connect_all(); 11 | XEM7010::synth(uut, target_path!("xem_7010/wire")); 12 | test_opalkelly_xem_wire_runtime( 13 | target_path!("xem_7010/wire/top.bit"), 14 | env!("XEM7010_SERIAL"), 15 | ) 16 | .unwrap(); 17 | } 18 | -------------------------------------------------------------------------------- /rust-hdl-core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-hdl-core" 3 | version = "0.46.0" 4 | edition = "2021" 5 | license = "MIT" 6 | description = "Write firmware for FPGAs in Rust - core crate" 7 | homepage = "https://rust-hdl.org" 8 | repository = "https://github.com/samitbasu/rust-hdl" 9 | keywords = ["fpga", "verilog", "hardware"] 10 | authors = ["Samit Basu "] 11 | 12 | [dependencies] 13 | rust-hdl-macros = { version = "0.46.0", path = "../rust-hdl-macros" } 14 | crossbeam = "0.8.1" 15 | num-bigint = "0.4.0" 16 | num-traits = "0.2.14" 17 | vcd = "0.6.1" 18 | evalexpr = "6.3.0" 19 | regex = "1.5.4" 20 | array-init = "2.0.0" 21 | rand = "0.8" 22 | petgraph = "0.6.0" 23 | embed-doc-image = "0.1.4" 24 | svg = "0.10.0" 25 | substring = "^1" 26 | anyhow = "^1" 27 | 28 | seq-macro = "0.3.1" 29 | serde = { version = "1.0.171", features = ["derive"] } 30 | -------------------------------------------------------------------------------- /rust-hdl-core/src/atom.rs: -------------------------------------------------------------------------------- 1 | use crate::ast::VerilogLiteral; 2 | use crate::constraint::PinConstraint; 3 | use crate::synth::VCDValue; 4 | use crate::type_descriptor::{TypeDescriptor, TypeKind}; 5 | 6 | #[doc(hidden)] 7 | #[derive(Copy, Clone, Debug, PartialEq)] 8 | pub enum AtomKind { 9 | InputParameter, 10 | OutputParameter, 11 | StubInputSignal, 12 | StubOutputSignal, 13 | Constant, 14 | LocalSignal, 15 | InOutParameter, 16 | OutputPassthrough, 17 | } 18 | 19 | impl AtomKind { 20 | pub fn is_parameter(&self) -> bool { 21 | matches!( 22 | self, 23 | AtomKind::InputParameter 24 | | AtomKind::OutputParameter 25 | | AtomKind::InOutParameter 26 | | AtomKind::OutputPassthrough 27 | ) 28 | } 29 | pub fn is_stub(&self) -> bool { 30 | matches!(self, AtomKind::StubInputSignal | AtomKind::StubOutputSignal) 31 | } 32 | } 33 | 34 | #[doc(hidden)] 35 | pub trait Atom { 36 | fn bits(&self) -> usize; 37 | fn connected(&self) -> bool; 38 | fn changed(&self) -> bool; 39 | fn kind(&self) -> AtomKind; 40 | fn descriptor(&self) -> TypeDescriptor; 41 | fn vcd(&self) -> VCDValue; 42 | fn id(&self) -> usize; 43 | fn verilog(&self) -> VerilogLiteral; 44 | fn constraints(&self) -> Vec; 45 | } 46 | 47 | pub fn is_atom_an_enum(atom: &dyn Atom) -> bool { 48 | matches!(atom.descriptor().kind, TypeKind::Enum(_)) 49 | } 50 | 51 | pub fn is_atom_signed(atom: &dyn Atom) -> bool { 52 | matches!(atom.descriptor().kind, TypeKind::Signed(_)) 53 | } 54 | 55 | pub fn get_atom_typename(atom: &dyn Atom) -> String { 56 | atom.descriptor().name 57 | } 58 | -------------------------------------------------------------------------------- /rust-hdl-core/src/check_error.rs: -------------------------------------------------------------------------------- 1 | use crate::block::Block; 2 | use crate::check_connected::check_connected; 3 | use crate::check_logic_loops::check_logic_loops; 4 | use crate::check_write_inputs::check_inputs_not_written; 5 | 6 | use std::collections::HashMap; 7 | 8 | /// A map of open connections, hashed on the signal ID 9 | pub type OpenMap = HashMap; 10 | 11 | /// Struct to capture a signal in the design for human consumption 12 | #[derive(Clone, Debug, PartialEq)] 13 | pub struct PathedName { 14 | /// The path to the signal (i.e., the hierarchical namespace such as `uut:flasher:blah`) 15 | pub path: String, 16 | /// The name of the signal that is being referenced, such as `pulse_in`. 17 | pub name: String, 18 | } 19 | 20 | /// A list of [PathedName] 21 | pub type PathedNameList = Vec; 22 | 23 | /// The enum models the errors that can be returned from "checking" 24 | /// a circuit using [check_all]. 25 | #[derive(Debug, Clone, PartialEq)] 26 | pub enum CheckError { 27 | /// The check failed because of one or more open signals (described by the [OpenMap]) 28 | OpenSignal(OpenMap), 29 | /// The circuit contains logical loops (i.e., `A <- B <- A`), and will not simulate 30 | LogicLoops(PathedNameList), 31 | /// The circuit attempts to write to the inputs, which is not allowed in RustHDL. 32 | WritesToInputs(PathedNameList), 33 | } 34 | 35 | /// This is a helper function used to check a [Block] for connection, loops, and 36 | /// writes to the inputs. 37 | /// ```rust 38 | /// use rust_hdl_core::prelude::*; 39 | /// 40 | /// #[derive(LogicBlock, Default)] 41 | /// struct Circuit { 42 | /// pub in1: Signal, 43 | /// pub out1: Signal, 44 | /// } 45 | /// 46 | /// impl Logic for Circuit { 47 | /// #[hdl_gen] 48 | /// fn update(&mut self) { 49 | /// self.out1.next = !self.in1.val(); 50 | /// } 51 | /// } 52 | /// 53 | /// let mut uut = Circuit::default(); uut.connect_all(); 54 | /// assert!(check_all(&uut).is_ok()); 55 | /// ``` 56 | pub fn check_all(uut: &dyn Block) -> Result<(), CheckError> { 57 | check_connected(uut)?; 58 | check_logic_loops(uut)?; 59 | check_inputs_not_written(uut)?; 60 | Ok(()) 61 | } 62 | -------------------------------------------------------------------------------- /rust-hdl-core/src/code_writer.rs: -------------------------------------------------------------------------------- 1 | #[derive(Default)] 2 | pub(crate) struct CodeWriter { 3 | lines: Vec<(usize, String)>, 4 | // Each line consists of an indent level and a string of text 5 | indent: usize, 6 | // CodeWriter provides a buffer for assembling lines too 7 | buffer: String, 8 | } 9 | 10 | impl CodeWriter { 11 | pub(crate) fn push(&mut self) { 12 | self.indent += 1 13 | } 14 | 15 | pub(crate) fn pop(&mut self) { 16 | self.indent -= 1 17 | } 18 | 19 | pub(crate) fn add_line>(&mut self, val: S) { 20 | self.lines.push((self.indent, String::from(val.as_ref()))) 21 | } 22 | 23 | pub(crate) fn add>(&mut self, val: S) { 24 | let temp = String::from(val.as_ref()); 25 | let pieces = temp.split_terminator('\n'); 26 | for piece in pieces { 27 | self.add_line(piece) 28 | } 29 | } 30 | 31 | pub(crate) fn write>(&mut self, val: S) { 32 | self.buffer += val.as_ref() 33 | } 34 | 35 | pub(crate) fn writeln>(&mut self, val: S) { 36 | self.write(val); 37 | self.flush(); 38 | } 39 | 40 | pub(crate) fn flush(&mut self) { 41 | let line = self.buffer.clone(); 42 | self.add(&line); 43 | self.buffer.clear() 44 | } 45 | } 46 | 47 | impl ToString for CodeWriter { 48 | fn to_string(&self) -> String { 49 | let mut buf = String::new(); 50 | for (indent, line) in &self.lines { 51 | buf += &" ".repeat(*indent); 52 | buf += line; 53 | buf += "\n" 54 | } 55 | buf 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /rust-hdl-core/src/direction.rs: -------------------------------------------------------------------------------- 1 | use crate::atom::AtomKind; 2 | 3 | #[doc(hidden)] 4 | pub trait Direction: Clone { 5 | const KIND: AtomKind; 6 | } 7 | 8 | /// This direction marker is used for a [Signal] that is 9 | /// an input with respect to a circuit. That means 10 | /// that we do not expect to write to the input, but that 11 | /// the value will be set by external components to the 12 | /// circuit. 13 | /// ```rust 14 | /// use rust_hdl_core::prelude::*; 15 | /// 16 | /// struct Foo { 17 | /// pub x: Signal, // <--- This is a single bit input 18 | /// pub y: Signal>, // <--- This is a multi-bit input signal 19 | /// } 20 | /// ``` 21 | #[derive(Default, Clone, Debug)] 22 | pub struct In {} 23 | 24 | /// This direction marker is used for a [Signal] that 25 | /// leaves a circuit as an output. That means we expect this 26 | /// circuit to drive the signal using its internal logic. 27 | /// It is an error in RustHDL to leave an output undriven. 28 | #[derive(Default, Clone, Debug)] 29 | pub struct Out {} 30 | 31 | #[derive(Default, Clone, Debug)] 32 | pub struct Local {} 33 | 34 | #[derive(Default, Clone, Debug)] 35 | pub struct InOut {} 36 | 37 | impl Direction for In { 38 | const KIND: AtomKind = AtomKind::InputParameter; 39 | } 40 | 41 | impl Direction for Out { 42 | const KIND: AtomKind = AtomKind::OutputParameter; 43 | } 44 | 45 | impl Direction for Local { 46 | const KIND: AtomKind = AtomKind::LocalSignal; 47 | } 48 | 49 | impl Direction for InOut { 50 | const KIND: AtomKind = AtomKind::InOutParameter; 51 | } 52 | -------------------------------------------------------------------------------- /rust-hdl-core/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod ast; 2 | #[doc(hidden)] 3 | pub mod atom; 4 | /// Module that supports arbitrary width bit vectors 5 | pub mod bits; 6 | #[doc(hidden)] 7 | pub mod bitvec; 8 | pub mod block; 9 | pub mod check_connected; 10 | pub mod check_error; 11 | pub mod check_logic_loops; 12 | pub mod check_timing; 13 | pub mod check_write_inputs; 14 | pub mod clock; 15 | pub mod code_writer; 16 | pub mod constant; 17 | pub mod constraint; 18 | pub mod direction; 19 | pub mod logic; 20 | pub mod module_defines; 21 | pub mod named_path; 22 | pub mod path_tools; 23 | pub mod prelude; 24 | pub mod probe; 25 | #[doc(hidden)] 26 | pub mod short_bit_vec; 27 | pub mod signal; 28 | pub mod signed; 29 | pub mod simulate; 30 | pub mod synth; 31 | pub mod timing; 32 | pub mod top_wrap; 33 | pub mod type_descriptor; 34 | pub mod vcd_probe; 35 | pub mod verilog_gen; 36 | pub mod verilog_visitor; 37 | pub mod yosys; 38 | -------------------------------------------------------------------------------- /rust-hdl-core/src/logic.rs: -------------------------------------------------------------------------------- 1 | use crate::ast::{Verilog, VerilogLink}; 2 | use crate::timing::TimingInfo; 3 | 4 | pub trait Logic { 5 | fn update(&mut self); 6 | fn connect(&mut self) {} 7 | fn hdl(&self) -> Verilog { 8 | Verilog::Empty 9 | } 10 | fn timing(&self) -> Vec { 11 | vec![] 12 | } 13 | } 14 | 15 | pub fn logic_connect_fn(x: &mut L) { 16 | x.connect(); 17 | } 18 | 19 | impl Logic for [L; P] { 20 | fn update(&mut self) {} 21 | } 22 | 23 | impl Logic for Vec { 24 | fn update(&mut self) {} 25 | } 26 | 27 | /* 28 | A link is always 29 | In --> In 30 | Out --> Out 31 | 32 | So if we have a piece of logic with: 33 | > In (A) ---- Link ---- > In(A) 34 | < Out (B) ---- Link ---- < Out(B) 35 | 36 | We want the connections for the internal parts to be 37 | handled automatically by RustHDL. So Dest In should always be 38 | driven, and source Out should always be driven. 39 | 40 | When externally connected, we assume that the situationis 41 | 42 | External Internal 43 | >Out (A) --- Link --->In(A) 44 | Vec; 49 | fn link_connect_source(&mut self); 50 | fn link_connect_dest(&mut self); 51 | } 52 | 53 | pub fn logic_connect_link_fn(source: &mut L, dest: &mut L) { 54 | source.link_connect_source(); 55 | dest.link_connect_dest(); 56 | } 57 | 58 | pub trait LogicJoin { 59 | fn join_connect(&mut self) {} 60 | fn join_hdl(_my_name: &str, _this: &str, _that: &str) -> Vec { 61 | vec![] 62 | } 63 | } 64 | 65 | pub fn logic_connect_join_fn(source: &mut L, dest: &mut K) { 66 | source.join_connect(); 67 | dest.join_connect(); 68 | } 69 | -------------------------------------------------------------------------------- /rust-hdl-core/src/named_path.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone, Debug, PartialEq, Default)] 2 | pub struct NamedPath { 3 | path: Vec, 4 | namespace: Vec, 5 | } 6 | 7 | impl NamedPath { 8 | pub fn push(&mut self, x: T) { 9 | self.path.push(x.to_string()); 10 | } 11 | 12 | pub fn pop(&mut self) { 13 | self.path.pop(); 14 | } 15 | 16 | pub fn parent(&self) -> String { 17 | self.path[0..self.path.len() - 1] 18 | .iter() 19 | .map(|x| x.to_string()) 20 | .collect::>() 21 | .join("$") 22 | } 23 | 24 | pub fn last(&self) -> String { 25 | self.path.last().unwrap().clone() 26 | } 27 | 28 | pub fn reset(&mut self) { 29 | self.path.clear(); 30 | } 31 | 32 | pub fn flat(&self, sep: &str) -> String { 33 | self.path.join(sep) 34 | } 35 | 36 | pub fn len(&self) -> usize { 37 | self.path.len() 38 | } 39 | 40 | pub fn is_empty(&self) -> bool { 41 | self.path.is_empty() 42 | } 43 | } 44 | 45 | impl ToString for NamedPath { 46 | fn to_string(&self) -> String { 47 | self.path.join("$") 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /rust-hdl-core/src/path_tools.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! target_path { 3 | ($name: expr) => { 4 | &std::path::PathBuf::from(env!("CARGO_TARGET_TMPDIR")) 5 | .join("firmware") 6 | .join($name) 7 | .to_string_lossy() 8 | .to_string() 9 | }; 10 | } 11 | 12 | #[macro_export] 13 | macro_rules! vcd_path { 14 | ($name: expr) => {{ 15 | let env = option_env!("CARGO_TARGET_TMPDIR").unwrap_or(env!("CARGO_MANIFEST_DIR")); 16 | let dest = &std::path::PathBuf::from(env).join("sims"); 17 | let _ = std::fs::create_dir(dest); 18 | dest.join($name).to_string_lossy().to_string() 19 | }}; 20 | } 21 | -------------------------------------------------------------------------------- /rust-hdl-core/src/probe.rs: -------------------------------------------------------------------------------- 1 | use crate::atom::Atom; 2 | use crate::block::Block; 3 | 4 | pub trait Probe { 5 | fn visit_start_scope(&mut self, _name: &str, _node: &dyn Block) {} 6 | fn visit_start_namespace(&mut self, _name: &str, _node: &dyn Block) {} 7 | fn visit_atom(&mut self, _name: &str, _signal: &dyn Atom) {} 8 | fn visit_end_namespace(&mut self, _name: &str, _node: &dyn Block) {} 9 | fn visit_end_scope(&mut self, _name: &str, _node: &dyn Block) {} 10 | } 11 | -------------------------------------------------------------------------------- /rust-hdl-core/src/timing.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone, Debug)] 2 | pub struct TimingInfo { 3 | pub name: String, 4 | pub clock: String, 5 | pub inputs: Vec, 6 | pub outputs: Vec, 7 | } 8 | -------------------------------------------------------------------------------- /rust-hdl-core/src/top_wrap.rs: -------------------------------------------------------------------------------- 1 | use crate::{ast::Verilog, block::Block, logic::Logic, probe::Probe, timing::TimingInfo}; 2 | 3 | pub struct TopWrap { 4 | pub uut: U, 5 | } 6 | 7 | impl TopWrap { 8 | pub fn new(uut: U) -> Self { 9 | Self { uut } 10 | } 11 | } 12 | 13 | impl Logic for TopWrap { 14 | fn update(&mut self) {} 15 | } 16 | 17 | impl Block for TopWrap { 18 | fn connect_all(&mut self) { 19 | self.connect(); 20 | self.uut.connect_all(); 21 | } 22 | fn update_all(&mut self) { 23 | self.update(); 24 | self.uut.update_all(); 25 | } 26 | fn has_changed(&self) -> bool { 27 | self.uut.has_changed() 28 | } 29 | fn accept(&self, name: &str, probe: &mut dyn Probe) { 30 | probe.visit_start_scope(name, self); 31 | self.uut.accept("uut", probe); 32 | probe.visit_end_scope(name, self); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /rust-hdl-core/src/type_descriptor.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone, Debug)] 2 | pub struct TypeDescriptor { 3 | pub name: String, 4 | pub kind: TypeKind, 5 | } 6 | 7 | #[derive(Clone, Debug)] 8 | pub struct TypeField { 9 | pub fieldname: String, 10 | pub kind: TypeDescriptor, 11 | } 12 | 13 | #[derive(Clone, Debug)] 14 | pub enum TypeKind { 15 | Bits(usize), 16 | Signed(usize), 17 | Enum(Vec), 18 | Composite(Vec>), 19 | } 20 | -------------------------------------------------------------------------------- /rust-hdl-core/tests/addnum_test.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl_core::prelude::*; 2 | 3 | #[test] 4 | fn test_addnum() { 5 | #[derive(LogicBlock)] 6 | struct AddNum { 7 | pub i1: Signal>, 8 | pub o1: Signal>, 9 | c1: Constant>, 10 | } 11 | 12 | impl Default for AddNum { 13 | fn default() -> Self { 14 | Self { 15 | i1: Default::default(), 16 | o1: Default::default(), 17 | c1: Constant::new(42.into()), 18 | } 19 | } 20 | } 21 | 22 | impl Logic for AddNum { 23 | #[hdl_gen] 24 | fn update(&mut self) { 25 | // Note that `self.c1.next` does not exist... 26 | self.o1.next = self.i1.val() + self.c1.val(); 27 | } 28 | } 29 | 30 | let mut sim: Simulation = Simulation::default(); 31 | sim.add_testbench(|mut ep: Sim| { 32 | let mut x = ep.init()?; 33 | x.i1.next = 13.into(); 34 | x = ep.wait(1, x)?; 35 | sim_assert_eq!(ep, x.o1.val(), 55, x); 36 | ep.done(x) 37 | }); 38 | 39 | let mut uut = AddNum::default(); 40 | uut.connect_all(); 41 | sim.run(Box::new(uut), 100).unwrap(); 42 | } 43 | -------------------------------------------------------------------------------- /rust-hdl-core/tests/struct_valued.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl_core::prelude::*; 2 | 3 | // We want to be able to combine a set of signals into a struct 4 | #[cfg(test)] 5 | #[derive(Clone, Copy, Debug, PartialEq, Eq, LogicState)] 6 | enum CmdType { 7 | Noop, 8 | Read, 9 | Write, 10 | } 11 | 12 | #[cfg(test)] 13 | #[derive(Copy, Clone, Default, Debug, PartialEq, Eq, LogicStruct)] 14 | struct MIGCmd { 15 | pub cmd: CmdType, 16 | pub active: Bit, 17 | pub len: Bits<6>, 18 | } 19 | 20 | #[test] 21 | fn test_composite() { 22 | assert_eq!(MIGCmd::BITS, 9); 23 | let x = MIGCmd { 24 | cmd: CmdType::Read, 25 | active: true, 26 | len: 35.into(), 27 | }; 28 | 29 | let y: Bits<9> = x.into(); 30 | assert_eq!(y.get_bits::<{ CmdType::BITS }>(0), 1); 31 | assert_eq!(y.get_bits::<{ bool::BITS }>(2), true); 32 | assert_eq!(y.get_bits::<6>(3), 35); 33 | let _x = MIGCmd { 34 | cmd: CmdType::Write, 35 | active: false, 36 | len: 30.into(), 37 | }; 38 | } 39 | 40 | #[derive(Clone, Debug, Default, Copy, PartialEq, LogicStruct)] 41 | struct CoreConfig { 42 | pub foo: Bits<6>, 43 | pub bar: Bits<32>, 44 | pub baz: Bits<16>, 45 | } 46 | 47 | #[derive(LogicBlock)] 48 | struct TestBlock { 49 | pub f: Signal>, 50 | pub g: Signal>, 51 | pub h: Signal>, 52 | vals: Constant, 53 | } 54 | 55 | impl Logic for TestBlock { 56 | #[hdl_gen] 57 | fn update(&mut self) { 58 | self.f.next = self.vals.val().foo; 59 | self.g.next = self.vals.val().bar; 60 | self.h.next = self.vals.val().baz; 61 | } 62 | } 63 | 64 | impl Default for TestBlock { 65 | fn default() -> Self { 66 | Self { 67 | f: Default::default(), 68 | g: Default::default(), 69 | h: Default::default(), 70 | vals: Constant::new(CoreConfig { 71 | foo: 7.into(), 72 | bar: 32.into(), 73 | baz: 8.into(), 74 | }), 75 | } 76 | } 77 | } 78 | 79 | #[test] 80 | fn test_test_block_synthesizes() { 81 | let mut uut = TestBlock::default(); 82 | uut.connect_all(); 83 | yosys_validate("test_block", &generate_verilog(&uut)).unwrap(); 84 | } 85 | -------------------------------------------------------------------------------- /rust-hdl-fpga-support/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-hdl-fpga-support" 3 | version = "0.46.0" 4 | edition = "2021" 5 | license = "MIT" 6 | description = "Support crate for RustHDL - provides FPGA specific code" 7 | homepage = "https://rust-hdl.org" 8 | repository = "https://github.com/samitbasu/rust-hdl" 9 | keywords = ["fpga", "verilog", "hardware"] 10 | authors = ["Samit Basu "] 11 | 12 | [dependencies] 13 | rust-hdl-core = { version = "0.46.0", path = "../rust-hdl-core" } 14 | rust-hdl-widgets = { version = "0.46.0", path = "../rust-hdl-widgets" } 15 | regex = { version = "^1.6.0" } 16 | -------------------------------------------------------------------------------- /rust-hdl-fpga-support/src/lattice/ecp5/edge_flip_flop.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl_core::prelude::*; 2 | 3 | #[derive(Clone, Debug, LogicBlock, Default)] 4 | pub struct EdgeFlipFlop { 5 | pub d: Signal, 6 | pub q: Signal, 7 | pub clock: Signal, 8 | } 9 | 10 | fn wrapper_once() -> &'static str { 11 | r##" 12 | OFS1P3DX inst_OFS1P3DX(.SCLK(clock), .SP(1'b1), .D(d), .Q(q), .CD(1'b0)); 13 | "## 14 | } 15 | 16 | fn wrapper_multiple(count: usize) -> String { 17 | (0..count) 18 | .map(|x| { 19 | format!( 20 | " 21 | OFS1P3DX ofs_{x}(.SCLK(clock), .SP(1'b1), .D(d[{x}]), .Q(q[{x}]), .CD(1'b0)); 22 | ", 23 | x = x 24 | ) 25 | }) 26 | .collect::>() 27 | .join("\n") 28 | } 29 | 30 | impl Logic for EdgeFlipFlop { 31 | fn update(&mut self) { 32 | if self.clock.pos_edge() { 33 | self.q.next = self.d.val() 34 | } 35 | } 36 | fn connect(&mut self) { 37 | self.q.connect(); 38 | } 39 | fn hdl(&self) -> Verilog { 40 | Verilog::Wrapper(Wrapper { 41 | code: if T::BITS == 1 { 42 | wrapper_once().to_string() 43 | } else { 44 | wrapper_multiple(T::BITS) 45 | }, 46 | cores: r##" 47 | (* blackbox *) 48 | module OFS1P3DX(input D, input SP, input SCLK, input CD, output Q); 49 | endmodule 50 | "## 51 | .into(), 52 | }) 53 | } 54 | } 55 | 56 | #[test] 57 | fn test_eflop_synthesizes() { 58 | let mut uut = EdgeFlipFlop::>::default(); 59 | uut.connect_all(); 60 | yosys_validate("eflop", &generate_verilog(&uut)).unwrap(); 61 | } 62 | -------------------------------------------------------------------------------- /rust-hdl-fpga-support/src/lattice/ecp5/io_delay.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl_core::prelude::*; 2 | 3 | #[derive(Clone, Debug, LogicBlock)] 4 | pub struct IODelay { 5 | pub a: Signal, 6 | pub z: Signal, 7 | _delay: u8, 8 | } 9 | 10 | impl IODelay { 11 | pub fn new(delay: u8) -> Self { 12 | Self { 13 | a: Default::default(), 14 | z: Default::default(), 15 | _delay: delay, 16 | } 17 | } 18 | } 19 | 20 | fn wrapper_once(delay: u8) -> String { 21 | format!( 22 | r##" 23 | defparam udel_dataini0.DEL_VALUE = {delay} ; 24 | defparam udel_dataini0.DEL_MODE = "USER_DEFINED" ; 25 | DELAYG udel_dataini0 (.A(buf_dataini0), .Z(dataini_t0)); 26 | "##, 27 | delay = delay 28 | ) 29 | } 30 | 31 | fn wrapper_multiple(count: usize, delay: u8) -> String { 32 | (0..count) 33 | .map(|x| { 34 | format!( 35 | r##" 36 | defparam udel_datain_{x}.DEL_VALUE = {delay} ; 37 | defparam udel_datain_{x}.DEL_MODE = "USER_DEFINED" ; 38 | DELAYG udel_datain_{x} (.A(a[{x}]), .Z(z[{x}])); 39 | "##, 40 | x = x, 41 | delay = delay 42 | ) 43 | }) 44 | .collect::>() 45 | .join("\n") 46 | } 47 | 48 | impl Logic for IODelay { 49 | fn update(&mut self) { 50 | self.z.next = self.a.val(); 51 | } 52 | fn connect(&mut self) { 53 | self.z.connect(); 54 | } 55 | fn hdl(&self) -> Verilog { 56 | Verilog::Wrapper(Wrapper { 57 | code: if T::BITS == 1 { 58 | wrapper_once(self._delay) 59 | } else { 60 | wrapper_multiple(T::BITS, self._delay) 61 | }, 62 | cores: r##" 63 | (* blackbox *) 64 | module DELAYG(input A, output Z); 65 | parameter DEL_MODE = "USER_DEFINED"; 66 | parameter DEL_VALUE = 0; 67 | endmodule 68 | "## 69 | .to_string(), 70 | }) 71 | } 72 | } 73 | 74 | #[test] 75 | fn test_iodelay_synthesizes() { 76 | let mut uut = IODelay::>::new(25); 77 | uut.connect_all(); 78 | yosys_validate("iodelay", &generate_verilog(&uut)).unwrap(); 79 | } 80 | -------------------------------------------------------------------------------- /rust-hdl-fpga-support/src/lattice/ecp5/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod edge_flip_flop; 2 | pub mod edge_tristate_buffer; 3 | pub mod edge_tristate_buffer_delayed; 4 | pub mod io_delay; 5 | pub mod oddr; 6 | pub mod output_buffer; 7 | -------------------------------------------------------------------------------- /rust-hdl-fpga-support/src/lattice/ecp5/oddr.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl_core::prelude::*; 2 | 3 | #[derive(Clone, Debug, LogicBlock, Default)] 4 | pub struct OutputDDR { 5 | pub d: Signal>, 6 | pub clock: Signal, 7 | pub q: Signal, 8 | pub reset: Signal, 9 | _capture: Bits<2>, 10 | } 11 | 12 | impl Logic for OutputDDR { 13 | fn update(&mut self) { 14 | if self.clock.pos_edge() { 15 | self._capture = self.d.val(); 16 | self.q.next = self._capture.get_bit(0); 17 | } 18 | if self.clock.neg_edge() { 19 | self.q.next = self._capture.get_bit(1); 20 | } 21 | if self.reset.val().into() { 22 | self._capture = 0.into(); 23 | self.q.next = false; 24 | } 25 | } 26 | fn connect(&mut self) { 27 | self.q.connect(); 28 | } 29 | fn hdl(&self) -> Verilog { 30 | Verilog::Wrapper(Wrapper { 31 | code: r##" 32 | ODDRX1F inst_ODDRX1F(.SCLK(clock), .RST(reset), .D0(d[0]), .D1(d[1]), .Q(q)); 33 | "## 34 | .into(), 35 | cores: r##" 36 | (* blackbox *) 37 | module ODDRX1F(input D0, input D1, input SCLK, input RST, output Q); 38 | endmodule 39 | "## 40 | .into(), 41 | }) 42 | } 43 | } 44 | 45 | #[test] 46 | fn test_oddr_synthesizes() { 47 | let mut uut = OutputDDR::default(); 48 | uut.connect_all(); 49 | yosys_validate("oddr", &generate_verilog(&uut)).unwrap(); 50 | } 51 | -------------------------------------------------------------------------------- /rust-hdl-fpga-support/src/lattice/ecp5/output_buffer.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl_core::prelude::*; 2 | 3 | #[derive(Clone, Debug, LogicBlock, Default)] 4 | pub struct OutputBuffer { 5 | pub i: Signal, 6 | pub o: Signal, 7 | } 8 | 9 | impl Logic for OutputBuffer { 10 | fn update(&mut self) { 11 | self.o.next = self.i.val(); 12 | } 13 | fn connect(&mut self) { 14 | self.o.connect(); 15 | } 16 | fn hdl(&self) -> Verilog { 17 | Verilog::Wrapper(Wrapper { 18 | code: r##" 19 | OB inst_OB(.I(i), .O(o)); 20 | "## 21 | .into(), 22 | cores: r##" 23 | (* blackbox *) 24 | module OB(input I, output O); 25 | endmodule 26 | "## 27 | .into(), 28 | }) 29 | } 30 | } 31 | 32 | #[test] 33 | fn test_output_buffer_synthesizes() { 34 | let mut uut = OutputBuffer::default(); 35 | uut.connect_all(); 36 | yosys_validate("obuf", &generate_verilog(&uut)).unwrap(); 37 | } 38 | -------------------------------------------------------------------------------- /rust-hdl-fpga-support/src/lattice/ice40/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod ice_pll; 2 | -------------------------------------------------------------------------------- /rust-hdl-fpga-support/src/lattice/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod ecp5; 2 | pub mod ice40; 3 | -------------------------------------------------------------------------------- /rust-hdl-fpga-support/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod lattice; 2 | pub mod toolchains; 3 | -------------------------------------------------------------------------------- /rust-hdl-fpga-support/src/toolchains/icestorm.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl_core::prelude::*; 2 | 3 | #[derive(Default)] 4 | struct PCFGenerator { 5 | path: NamedPath, 6 | namespace: NamedPath, 7 | pcf: Vec, 8 | } 9 | 10 | impl Probe for PCFGenerator { 11 | fn visit_start_scope(&mut self, name: &str, _node: &dyn Block) { 12 | let _top_level = self.path.to_string(); 13 | self.path.push(name); 14 | self.namespace.reset(); 15 | } 16 | fn visit_start_namespace(&mut self, name: &str, _node: &dyn Block) { 17 | self.namespace.push(name); 18 | } 19 | fn visit_atom(&mut self, name: &str, signal: &dyn Atom) { 20 | if self.path.len() == 1 { 21 | let namespace = self.namespace.flat("$"); 22 | let name = if namespace.is_empty() { 23 | name.to_owned() 24 | } else { 25 | format!("{}${}", namespace, name) 26 | }; 27 | for pin in &signal.constraints() { 28 | match &pin.constraint { 29 | Constraint::Location(l) => { 30 | if signal.bits() == 1 { 31 | self.pcf.push(format!("set_io {} {}", name, l)) 32 | } else { 33 | self.pcf 34 | .push(format!("set_io {}[{}] {}", name, pin.index, l)) 35 | } 36 | } 37 | Constraint::Custom(s) => self.pcf.push(s.clone()), 38 | _ => { 39 | panic!("Pin constraint type {:?} is unsupported!", pin.constraint) 40 | } 41 | } 42 | } 43 | } 44 | } 45 | fn visit_end_namespace(&mut self, _name: &str, _node: &dyn Block) { 46 | self.namespace.pop(); 47 | } 48 | fn visit_end_scope(&mut self, _name: &str, _node: &dyn Block) { 49 | self.path.pop(); 50 | } 51 | } 52 | 53 | pub fn generate_pcf(uut: &U) -> String { 54 | let mut pcf = PCFGenerator::default(); 55 | uut.accept("top", &mut pcf); 56 | pcf.pcf.join("\n") + "\n" 57 | } 58 | -------------------------------------------------------------------------------- /rust-hdl-fpga-support/src/toolchains/mod.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl_core::prelude::*; 2 | 3 | pub fn map_signal_type_to_lattice_string(k: &SignalType) -> &str { 4 | match k { 5 | SignalType::LowVoltageCMOS_3v3 => "LVCMOS33", 6 | _ => panic!( 7 | "Unsupported mapping for signal type {:?} in Lattice mapping", 8 | k 9 | ), 10 | } 11 | } 12 | 13 | pub fn map_signal_type_to_xilinx_string(k: &SignalType) -> &str { 14 | match k { 15 | SignalType::LowVoltageCMOS_1v8 => "LVCMOS18", 16 | SignalType::LowVoltageCMOS_3v3 => "LVCMOS33", 17 | SignalType::StubSeriesTerminatedLogic_II => "SSTL18_II", 18 | SignalType::DifferentialStubSeriesTerminatedLogic_II => "DIFF_SSTL18_II", 19 | SignalType::StubSeriesTerminatedLogic_II_No_Termination => "SSTL18_II | IN_TERM=NONE", 20 | SignalType::DifferentialStubSeriesTerminatedLogic_II_No_Termination => { 21 | "DIFF_SSTL18_II | IN_TERM=NONE" 22 | } 23 | SignalType::Custom(c) => c, 24 | SignalType::LowVoltageDifferentialSignal_2v5 => "LVDS_25", 25 | SignalType::StubSeriesTerminatedLogic_1v5 => "SSTL15", 26 | SignalType::LowVoltageCMOS_1v5 => "LVCMOS15", 27 | SignalType::DifferentialStubSeriesTerminatedLogic_1v5 => "DIFF_SSTL15", 28 | } 29 | } 30 | 31 | pub mod ecp5; 32 | pub mod icestorm; 33 | pub mod ise; 34 | pub mod vivado; 35 | -------------------------------------------------------------------------------- /rust-hdl-hls/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-hdl-hls" 3 | version = "0.46.0" 4 | edition = "2021" 5 | license = "MIT" 6 | description = "Write firmware for FPGAs in Rust - High Level Synthesis crate" 7 | homepage = "https://rust-hdl.org" 8 | repository = "https://github.com/samitbasu/rust-hdl" 9 | keywords = ["fpga", "verilog", "hardware"] 10 | authors = ["Samit Basu "] 11 | 12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 13 | 14 | [dependencies] 15 | rust-hdl-core = { version = "0.46.0", path = "../rust-hdl-core" } 16 | rust-hdl-widgets = { version = "0.46.0", path = "../rust-hdl-widgets" } 17 | array-init = { version = "2.0.0" } 18 | rand = "0.8" 19 | -------------------------------------------------------------------------------- /rust-hdl-hls/src/expander.rs: -------------------------------------------------------------------------------- 1 | use crate::bus::{FIFOReadController, FIFOWriteController}; 2 | use rust_hdl_core::prelude::*; 3 | use rust_hdl_widgets::prelude::*; 4 | 5 | #[derive(LogicBlock)] 6 | pub struct Expander { 7 | pub bus_read: FIFOReadController>, 8 | pub bus_write: FIFOWriteController>, 9 | pub clock: Signal, 10 | expander: FIFOExpanderN, 11 | } 12 | 13 | impl Logic for Expander { 14 | #[hdl_gen] 15 | fn update(&mut self) { 16 | clock!(self, clock, expander); 17 | // Connect the HLS read bus to the expanders native signals 18 | self.bus_read.read.next = self.expander.read.val(); 19 | self.expander.empty.next = self.bus_read.empty.val(); 20 | self.expander.data_in.next = self.bus_read.data.val(); 21 | // Connect the HLS write bus to the expanders native signals 22 | self.expander.full.next = self.bus_write.full.val(); 23 | self.bus_write.data.next = self.expander.data_out.val(); 24 | self.bus_write.write.next = self.expander.write.val(); 25 | } 26 | } 27 | 28 | impl Expander { 29 | pub fn new(order: WordOrder) -> Self { 30 | Self { 31 | bus_read: Default::default(), 32 | bus_write: Default::default(), 33 | clock: Default::default(), 34 | expander: FIFOExpanderN::new(order), 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /rust-hdl-hls/src/fifo_linker.rs: -------------------------------------------------------------------------------- 1 | use crate::bus::{FIFOReadController, FIFOWriteController}; 2 | use rust_hdl_core::prelude::*; 3 | 4 | #[derive(LogicBlock, Default)] 5 | pub struct FIFOLink { 6 | pub read: FIFOReadController, 7 | pub write: FIFOWriteController, 8 | will_transfer: Signal, 9 | } 10 | 11 | impl Logic for FIFOLink { 12 | #[hdl_gen] 13 | fn update(&mut self) { 14 | self.will_transfer.next = !self.read.empty.val() & !self.write.full.val(); 15 | self.write.data.next = self.read.data.val(); 16 | self.read.read.next = self.will_transfer.val(); 17 | self.write.write.next = self.will_transfer.val(); 18 | } 19 | } 20 | 21 | #[test] 22 | fn test_link_synthesizes() { 23 | let mut uut: FIFOLink> = Default::default(); 24 | uut.connect_all(); 25 | yosys_validate("fifo_link", &generate_verilog(&uut)).unwrap(); 26 | } 27 | -------------------------------------------------------------------------------- /rust-hdl-hls/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod bidi; 2 | pub mod bridge; 3 | pub mod bus; 4 | pub mod controller; 5 | pub mod cross_fifo; 6 | pub mod expander; 7 | pub mod fifo; 8 | pub mod fifo_linker; 9 | pub mod host; 10 | pub mod miso_fifo_port; 11 | pub mod miso_port; 12 | pub mod miso_wide_port; 13 | pub mod mosi_fifo_port; 14 | pub mod mosi_port; 15 | pub mod mosi_wide_port; 16 | pub mod prelude; 17 | pub mod reducer; 18 | pub mod router; 19 | pub mod router_rom; 20 | pub mod sdram_controller; 21 | pub mod sdram_controller_tester; 22 | pub mod sdram_fifo; 23 | pub mod sim; 24 | pub mod spi; 25 | pub mod test_helpers; 26 | 27 | pub trait HLSNamedPorts { 28 | fn ports(&self) -> Vec; 29 | } 30 | -------------------------------------------------------------------------------- /rust-hdl-hls/src/miso_fifo_port.rs: -------------------------------------------------------------------------------- 1 | use crate::bus::{FIFOWriteResponder, SoCPortResponder}; 2 | use crate::fifo::SyncFIFO; 3 | use crate::miso_port::MISOPort; 4 | use rust_hdl_core::prelude::*; 5 | 6 | #[derive(LogicBlock, Default)] 7 | pub struct MISOFIFOPort { 8 | pub bus: SoCPortResponder, 9 | port: MISOPort, 10 | fifo: SyncFIFO, N, NP1, BLOCK>, 11 | pub fifo_bus: FIFOWriteResponder>, 12 | } 13 | 14 | impl Logic 15 | for MISOFIFOPort 16 | { 17 | #[hdl_gen] 18 | fn update(&mut self) { 19 | SoCPortResponder::::link(&mut self.bus, &mut self.port.bus); 20 | self.fifo.clock.next = self.bus.clock.val(); 21 | self.fifo.bus_read.read.next = self.port.strobe_out.val(); 22 | self.port.ready_in.next = !self.fifo.bus_read.empty.val(); 23 | self.port.port_in.next = self.fifo.bus_read.data.val(); 24 | FIFOWriteResponder::>::link(&mut self.fifo_bus, &mut self.fifo.bus_write); 25 | } 26 | } 27 | 28 | #[test] 29 | fn test_miso_fifo_port_is_synthesizable() { 30 | let mut dev = MISOFIFOPort::<16, 4, 5, 1>::default(); 31 | dev.bus.link_connect_dest(); 32 | dev.fifo_bus.link_connect_dest(); 33 | dev.connect_all(); 34 | let vlog = generate_verilog(&dev); 35 | yosys_validate("miso_fifo_port", &vlog).unwrap(); 36 | } 37 | -------------------------------------------------------------------------------- /rust-hdl-hls/src/miso_port.rs: -------------------------------------------------------------------------------- 1 | // A simple, local bus for attaching stuff together on the FPGA 2 | use crate::bus::SoCPortResponder; 3 | use rust_hdl_core::prelude::*; 4 | use rust_hdl_widgets::prelude::*; 5 | 6 | // An input port simply stores the value written to it's input back to 7 | // the master. The address comparison logic is registered to improve the 8 | // timing analysis of the bus. 9 | #[derive(LogicBlock, Default)] 10 | pub struct MISOPort { 11 | pub bus: SoCPortResponder, 12 | pub port_in: Signal>, 13 | pub clock_out: Signal, 14 | pub ready_in: Signal, 15 | pub strobe_out: Signal, 16 | address_active: DFF, 17 | } 18 | 19 | impl Logic for MISOPort { 20 | #[hdl_gen] 21 | fn update(&mut self) { 22 | self.clock_out.next = self.bus.clock.val(); 23 | dff_setup!(self, clock_out, address_active); 24 | self.address_active.d.next = self.bus.select.val(); 25 | self.bus.to_controller.next = 0.into(); 26 | self.bus.ready.next = false; 27 | self.strobe_out.next = false; 28 | if self.address_active.q.val() { 29 | self.bus.ready.next = self.ready_in.val(); 30 | self.bus.to_controller.next = self.port_in.val(); 31 | self.strobe_out.next = self.bus.strobe.val(); 32 | } 33 | } 34 | } 35 | 36 | #[test] 37 | fn test_local_in_port_is_synthesizable() { 38 | let mut dev = MISOPort::<16>::default(); 39 | dev.connect_all(); 40 | let vlog = generate_verilog(&dev); 41 | yosys_validate("localin", &vlog).unwrap(); 42 | } 43 | -------------------------------------------------------------------------------- /rust-hdl-hls/src/mosi_fifo_port.rs: -------------------------------------------------------------------------------- 1 | use crate::bus::{FIFOReadResponder, SoCPortResponder}; 2 | use crate::fifo::SyncFIFO; 3 | use crate::mosi_port::MOSIPort; 4 | use rust_hdl_core::prelude::*; 5 | 6 | #[derive(LogicBlock, Default)] 7 | pub struct MOSIFIFOPort { 8 | pub bus: SoCPortResponder, 9 | port: MOSIPort, 10 | fifo: SyncFIFO, N, NP1, BLOCK>, 11 | pub fifo_bus: FIFOReadResponder>, 12 | } 13 | 14 | impl Logic 15 | for MOSIFIFOPort 16 | { 17 | #[hdl_gen] 18 | fn update(&mut self) { 19 | SoCPortResponder::::link(&mut self.bus, &mut self.port.bus); 20 | self.fifo.clock.next = self.bus.clock.val(); 21 | self.fifo.bus_write.data.next = self.port.port_out.val(); 22 | self.fifo.bus_write.write.next = self.port.strobe_out.val(); 23 | self.port.ready.next = !self.fifo.bus_write.full.val(); 24 | FIFOReadResponder::>::link(&mut self.fifo_bus, &mut self.fifo.bus_read); 25 | } 26 | } 27 | 28 | #[test] 29 | fn test_mosi_fifo_port_is_synthesizable() { 30 | let mut dev = MOSIFIFOPort::<16, 4, 5, 1>::default(); 31 | dev.bus.link_connect_dest(); 32 | dev.fifo_bus.link_connect_dest(); 33 | dev.connect_all(); 34 | let vlog = generate_verilog(&dev); 35 | yosys_validate("mosi_fifo_port", &vlog).unwrap(); 36 | } 37 | -------------------------------------------------------------------------------- /rust-hdl-hls/src/mosi_port.rs: -------------------------------------------------------------------------------- 1 | use crate::bus::SoCPortResponder; 2 | use rust_hdl_core::prelude::*; 3 | use rust_hdl_widgets::prelude::*; 4 | 5 | // An output port simply stores the value written to that memory location 6 | // by the master. The value is latched. 7 | // The strobe from the master is also forwarded. This allows you to 8 | // build logic that knows when the value was changed, or treat the 9 | // strobe like a trigger. 10 | #[derive(LogicBlock, Default)] 11 | pub struct MOSIPort { 12 | pub bus: SoCPortResponder, 13 | pub port_out: Signal>, 14 | pub strobe_out: Signal, 15 | pub ready: Signal, 16 | pub clock_out: Signal, 17 | state: DFF>, 18 | address_active: DFF, 19 | strobe: DFF, 20 | } 21 | 22 | impl Logic for MOSIPort { 23 | #[hdl_gen] 24 | fn update(&mut self) { 25 | self.clock_out.next = self.bus.clock.val(); 26 | dff_setup!(self, clock_out, state, address_active, strobe); 27 | self.port_out.next = self.state.q.val(); 28 | self.address_active.d.next = self.bus.select.val(); 29 | self.bus.ready.next = false; 30 | self.strobe_out.next = self.strobe.q.val(); 31 | self.strobe.d.next = false; 32 | if self.address_active.q.val() { 33 | self.bus.ready.next = self.ready.val() & self.bus.select.val(); 34 | if self.bus.strobe.val() { 35 | self.state.d.next = self.bus.from_controller.val(); 36 | } 37 | self.strobe.d.next = self.bus.strobe.val() & self.ready.val(); 38 | } 39 | self.bus.to_controller.next = 0.into(); 40 | } 41 | } 42 | 43 | #[test] 44 | fn test_local_out_port_is_synthesizable() { 45 | let mut dev = MOSIPort::<16>::default(); 46 | dev.connect_all(); 47 | let vlog = generate_verilog(&dev); 48 | yosys_validate("localout", &vlog).unwrap(); 49 | } 50 | -------------------------------------------------------------------------------- /rust-hdl-hls/src/prelude.rs: -------------------------------------------------------------------------------- 1 | pub use crate::bidi::{BidiBusD, BidiBusM, BidiMaster, BidiSimulatedDevice}; 2 | pub use crate::bridge::Bridge; 3 | pub use crate::bus::{ 4 | FIFOReadController, FIFOReadResponder, FIFOWriteController, FIFOWriteResponder, 5 | SoCBusController, SoCBusResponder, SoCPortController, SoCPortResponder, 6 | }; 7 | pub use crate::bus_address_strobe; 8 | pub use crate::bus_write_strobe; 9 | pub use crate::controller::BaseController; 10 | pub use crate::cross_fifo::{CrossNarrow, CrossWiden}; 11 | pub use crate::expander::Expander; 12 | pub use crate::fifo::{AsyncFIFO, SyncFIFO}; 13 | pub use crate::fifo_linker::FIFOLink; 14 | pub use crate::hls_fifo_read; 15 | pub use crate::hls_fifo_read_lazy; 16 | pub use crate::hls_fifo_write; 17 | pub use crate::hls_fifo_write_lazy; 18 | pub use crate::hls_host_drain; 19 | pub use crate::hls_host_get_word; 20 | pub use crate::hls_host_get_words; 21 | pub use crate::hls_host_issue_read; 22 | pub use crate::hls_host_noop; 23 | pub use crate::hls_host_ping; 24 | pub use crate::hls_host_put_word; 25 | pub use crate::hls_host_write; 26 | pub use crate::host::Host; 27 | pub use crate::miso_fifo_port::MISOFIFOPort; 28 | pub use crate::miso_port::MISOPort; 29 | pub use crate::miso_wide_port::MISOWidePort; 30 | pub use crate::mosi_fifo_port::MOSIFIFOPort; 31 | pub use crate::mosi_port::MOSIPort; 32 | pub use crate::mosi_wide_port::MOSIWidePort; 33 | pub use crate::reducer::Reducer; 34 | pub use crate::router::Router; 35 | pub use crate::router_rom::*; 36 | pub use crate::sdram_controller::SDRAMController; 37 | pub use crate::sdram_controller_tester::SDRAMControllerTester; 38 | pub use crate::sdram_fifo::SDRAMFIFO; 39 | pub use crate::spi::HLSSPIMaster; 40 | pub use crate::spi::HLSSPIMasterDynamicMode; 41 | pub use crate::spi::{HLSSPIMuxMasters, HLSSPIMuxSlaves}; 42 | pub use crate::test_helpers::*; 43 | pub use crate::HLSNamedPorts; 44 | -------------------------------------------------------------------------------- /rust-hdl-hls/src/reducer.rs: -------------------------------------------------------------------------------- 1 | use crate::bus::{FIFOReadController, FIFOWriteController}; 2 | use rust_hdl_core::prelude::*; 3 | use rust_hdl_widgets::prelude::{FIFOReducerN, WordOrder}; 4 | 5 | #[derive(LogicBlock)] 6 | pub struct Reducer { 7 | pub bus_read: FIFOReadController>, 8 | pub bus_write: FIFOWriteController>, 9 | pub clock: Signal, 10 | reducer: FIFOReducerN, 11 | } 12 | 13 | impl Logic for Reducer { 14 | #[hdl_gen] 15 | fn update(&mut self) { 16 | // Connect the clock 17 | clock!(self, clock, reducer); 18 | // Connect the HLS read bus to the native signals 19 | self.bus_read.read.next = self.reducer.read.val(); 20 | self.reducer.empty.next = self.bus_read.empty.val(); 21 | self.reducer.data_in.next = self.bus_read.data.val(); 22 | // Connect the HDL write bus to the native signals 23 | self.reducer.full.next = self.bus_write.full.val(); 24 | self.bus_write.data.next = self.reducer.data_out.val(); 25 | self.bus_write.write.next = self.reducer.write.val(); 26 | } 27 | } 28 | 29 | impl Reducer { 30 | pub fn new(order: WordOrder) -> Self { 31 | Self { 32 | bus_read: Default::default(), 33 | bus_write: Default::default(), 34 | clock: Default::default(), 35 | reducer: FIFOReducerN::new(order), 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /rust-hdl-macros/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | -------------------------------------------------------------------------------- /rust-hdl-macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-hdl-macros" 3 | version = "0.46.0" 4 | edition = "2018" 5 | license = "MIT" 6 | description = "Macro support for RustHDL" 7 | homepage = "https://rust-hdl.org" 8 | repository = "https://github.com/samitbasu/rust-hdl" 9 | keywords = ["fpga", "verilog", "hardware"] 10 | authors = ["Samit Basu "] 11 | 12 | [lib] 13 | proc-macro = true 14 | 15 | [dependencies] 16 | syn = { version = "1.0.73", features = ["full", "extra-traits", "visit"] } 17 | quote = "1.0.9" 18 | proc-macro2 = "1.0.27" 19 | regex = "1.3.4" 20 | -------------------------------------------------------------------------------- /rust-hdl-macros/src/logic_block.rs: -------------------------------------------------------------------------------- 1 | use quote::quote; 2 | use syn::Result; 3 | 4 | use crate::common; 5 | use crate::common::TS; 6 | 7 | pub(crate) fn get_impl_for_logic_block(input: &syn::DeriveInput) -> Result { 8 | let fields = common::get_field_names(input)?; 9 | let update_all = common::get_update_all(fields.clone())?; 10 | let has_changed = common::get_has_changed(fields.clone())?; 11 | let connect_all = common::get_connect_all(fields.clone())?; 12 | let accept = get_accept(fields)?; 13 | let name = &input.ident; 14 | let (impl_generics, ty_generics, _where_clause) = &input.generics.split_for_impl(); 15 | Ok(quote! { 16 | impl #impl_generics block::Block for #name #ty_generics { 17 | #connect_all 18 | #update_all 19 | #has_changed 20 | #accept 21 | } 22 | }) 23 | } 24 | 25 | fn get_accept(fields: Vec) -> Result { 26 | let fields_as_strings = fields.iter().map(|x| x.to_string()).collect::>(); 27 | Ok(quote! { 28 | fn accept(&self, name: &str, probe: &mut dyn probe::Probe) { 29 | probe.visit_start_scope(name, self); 30 | #(self.#fields.accept(#fields_as_strings, probe);)* 31 | probe.visit_end_scope(name, self); 32 | } 33 | }) 34 | } 35 | -------------------------------------------------------------------------------- /rust-hdl-ok-core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-hdl-ok-core" 3 | version = "0.46.0" 4 | edition = "2021" 5 | license = "MIT" 6 | description = "Generic support code for OpalKelly based FPGA modules that use the FrontPanel HDL interface" 7 | homepage = "https://rust-hdl.org" 8 | repository = "https://github.com/samitbasu/rust-hdl" 9 | keywords = ["fpga", "verilog", "hardware"] 10 | authors = ["Samit Basu "] 11 | 12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 13 | 14 | [dependencies] 15 | rust-hdl-core = { version = "0.46.0", path = "../rust-hdl-core" } 16 | rust-hdl-hls = { version = "0.46.0", path = "../rust-hdl-hls" } 17 | rust-hdl-sim = { version = "0.46.0", path = "../rust-hdl-sim" } 18 | rust-hdl-widgets = { version = "0.46.0", path = "../rust-hdl-widgets" } 19 | rust-hdl-ok-frontpanel-sys = { version = "0.46.0", path = "../rust-hdl-ok-frontpanel-sys" } 20 | regex = "1.5.4" 21 | rand = "0.8.5" 22 | -------------------------------------------------------------------------------- /rust-hdl-ok-core/src/core/bsp.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl_core::prelude::*; 2 | 3 | use super::OpalKellyHost; 4 | use super::OpalKellyHostInterface; 5 | 6 | pub trait OpalKellyBSP { 7 | fn hi() -> OpalKellyHostInterface; 8 | fn ok_host() -> OpalKellyHost; 9 | fn leds() -> Signal>; 10 | fn clocks() -> Vec>; 11 | fn synth(uut: U, dir: &str); 12 | } 13 | -------------------------------------------------------------------------------- /rust-hdl-ok-core/src/core/clock.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl_core::prelude::*; 2 | 3 | #[derive(LogicBlock, Default)] 4 | pub struct ClockBuffer { 5 | pub clock_in: Signal, 6 | pub clock_out: Signal, 7 | } 8 | 9 | impl Logic for ClockBuffer { 10 | fn update(&mut self) {} 11 | 12 | fn connect(&mut self) { 13 | self.clock_out.connect(); 14 | } 15 | 16 | fn hdl(&self) -> Verilog { 17 | Verilog::Wrapper(Wrapper { 18 | code: r#" 19 | BUFG bufg_inst(.I(clock_in), .O(clock_out)); 20 | "# 21 | .to_string(), 22 | cores: r#" 23 | (* blackbox *) 24 | module BUFG (O, I); 25 | output O; 26 | input I; 27 | endmodule 28 | "# 29 | .to_string(), 30 | }) 31 | } 32 | } 33 | 34 | #[test] 35 | fn test_clock_buffer() { 36 | let mut uut = ClockBuffer::default(); 37 | uut.connect_all(); 38 | let vlog = generate_verilog(&uut); 39 | yosys_validate("bufg", &vlog).unwrap(); 40 | } 41 | -------------------------------------------------------------------------------- /rust-hdl-ok-core/src/core/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_camel_case_types)] 2 | 3 | use ok_hi::OpalKellyHostInterface; 4 | use ok_host::OpalKellyHost; 5 | 6 | pub mod bsp; 7 | pub mod clock; 8 | pub mod ok_download; 9 | pub mod ok_hi; 10 | pub mod ok_hls_bridge; 11 | pub mod ok_host; 12 | pub mod ok_pipe; 13 | pub mod ok_trigger; 14 | pub mod ok_wire; 15 | pub mod prelude; 16 | pub mod spi; 17 | pub mod tools; 18 | 19 | pub const MHZ48: u64 = 48_000_000; 20 | -------------------------------------------------------------------------------- /rust-hdl-ok-core/src/core/prelude.rs: -------------------------------------------------------------------------------- 1 | pub use super::bsp::*; 2 | pub use super::ok_download::*; 3 | pub use super::ok_hi::*; 4 | pub use super::ok_host::*; 5 | pub use super::ok_pipe::*; 6 | pub use super::ok_trigger::*; 7 | pub use super::ok_wire::*; 8 | pub use super::spi::*; 9 | pub use super::tools::*; 10 | 11 | pub const MHZ48: u64 = 48_000_000; 12 | pub const MHZ100: u64 = 100_000_000; 13 | -------------------------------------------------------------------------------- /rust-hdl-ok-core/src/core/tools.rs: -------------------------------------------------------------------------------- 1 | pub fn find_ok_bus_collisions(vlog: &str) { 2 | let expr = regex::Regex::new(r#"\.ep_addr\(8'h(\w+)\)"#).unwrap(); 3 | let mut addr_list = vec![]; 4 | for capture in expr.captures_iter(vlog) { 5 | let port = capture.get(1).unwrap().as_str(); 6 | assert!( 7 | !addr_list.contains(&port.to_string()), 8 | "Found duplicate port! {}", 9 | port 10 | ); 11 | addr_list.push(port.to_owned()); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /rust-hdl-ok-core/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod core; 2 | pub mod test_common; 3 | pub use rust_hdl_ok_frontpanel_sys as frontpanel; 4 | -------------------------------------------------------------------------------- /rust-hdl-ok-core/src/test_common/blinky.rs: -------------------------------------------------------------------------------- 1 | use crate::core::prelude::*; 2 | use rust_hdl_core::prelude::*; 3 | use rust_hdl_widgets::prelude::*; 4 | use std::time::Duration; 5 | 6 | #[derive(LogicBlock)] 7 | pub struct OpalKellyBlinky { 8 | pub hi: OpalKellyHostInterface, 9 | pub ok_host: OpalKellyHost, 10 | pub led: Signal>, 11 | pub pulser: Pulser, 12 | } 13 | 14 | impl OpalKellyBlinky { 15 | pub fn new() -> Self { 16 | Self { 17 | hi: B::hi(), 18 | ok_host: B::ok_host(), 19 | led: B::leds(), 20 | pulser: Pulser::new(MHZ48, 1.0, Duration::from_millis(500)), 21 | } 22 | } 23 | } 24 | 25 | impl Logic for OpalKellyBlinky { 26 | #[hdl_gen] 27 | fn update(&mut self) { 28 | OpalKellyHostInterface::link(&mut self.hi, &mut self.ok_host.hi); 29 | self.pulser.clock.next = self.ok_host.ti_clk.val(); 30 | self.pulser.enable.next = true; 31 | if self.pulser.pulse.val() { 32 | self.led.next = 0xFF.into(); 33 | } else { 34 | self.led.next = 0x00.into(); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /rust-hdl-ok-core/src/test_common/ddr.rs: -------------------------------------------------------------------------------- 1 | use crate::test_common::tools::ok_test_prelude; 2 | use rust_hdl_ok_frontpanel_sys::{make_u16_buffer, OkError}; 3 | use std::thread::sleep; 4 | use std::time::{Duration, Instant}; 5 | 6 | pub fn test_opalkelly_ddr_stress_runtime( 7 | bit_file: &str, 8 | serial_number: &str, 9 | ) -> Result<(), OkError> { 10 | let hnd = ok_test_prelude(bit_file, serial_number)?; 11 | hnd.reset_firmware(0); 12 | sleep(Duration::from_millis(100)); 13 | hnd.set_wire_in(1, 1); 14 | hnd.update_wire_ins(); 15 | // Read the data in 256*2 = 512 byte blocks 16 | let mut counter = 0; 17 | for _ in 0..32 { 18 | let mut data = vec![0_u8; 1024 * 1024]; 19 | let now = Instant::now(); 20 | hnd.read_from_block_pipe_out(0xA0, 256, &mut data).unwrap(); 21 | let elapsed = (Instant::now() - now).as_micros(); 22 | println!( 23 | "Download rate is {} mbps", 24 | (data.len() as f32 * 8.0) / (elapsed as f32 * 1e-6) / 1e6 25 | ); 26 | let data_shorts = make_u16_buffer(&data); 27 | let mut data_words = vec![]; 28 | for i in 0..data_shorts.len() / 2 { 29 | let lo_word = data_shorts[2 * i] as u32; 30 | let hi_word = data_shorts[2 * i + 1] as u32; 31 | data_words.push((hi_word << 16) | lo_word); 32 | } 33 | for val in data_words { 34 | assert_eq!(((counter as u128) & 0xFFFFFFFF_u128) as u32, val); 35 | counter += 1; 36 | } 37 | } 38 | hnd.set_wire_in(1, 0); 39 | hnd.update_wire_ins(); 40 | hnd.close(); 41 | Ok(()) 42 | } 43 | -------------------------------------------------------------------------------- /rust-hdl-ok-core/src/test_common/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use std::collections::BTreeMap; 4 | use std::f64::consts::PI; 5 | 6 | use rust_hdl_core::prelude::*; 7 | use rust_hdl_widgets::prelude::*; 8 | 9 | pub mod blinky; 10 | 11 | pub mod ddr; 12 | 13 | pub mod download; 14 | //pub mod fifo_tester; 15 | 16 | pub mod fir; 17 | 18 | pub mod mux_spi; 19 | 20 | pub mod pipe; 21 | pub mod soc; 22 | 23 | pub mod spi; 24 | 25 | pub mod tools; 26 | 27 | pub mod wave; 28 | 29 | pub mod wire; 30 | -------------------------------------------------------------------------------- /rust-hdl-ok-core/src/test_common/prelude.rs: -------------------------------------------------------------------------------- 1 | pub use super::blinky::*; 2 | pub use super::ddr::*; 3 | pub use super::download::*; 4 | pub use super::mux_spi::*; 5 | pub use super::pipe::*; 6 | pub use super::spi::*; 7 | pub use super::tools::*; 8 | pub use super::wave::*; 9 | pub use super::wire::*; 10 | 11 | -------------------------------------------------------------------------------- /rust-hdl-ok-core/src/test_common/wave.rs: -------------------------------------------------------------------------------- 1 | use crate::core::prelude::*; 2 | use rust_hdl_core::prelude::*; 3 | use rust_hdl_hls::test_helpers::FaderWithSyncROM; 4 | 5 | #[derive(LogicBlock)] 6 | pub struct OpalKellyWave { 7 | pub hi: OpalKellyHostInterface, 8 | pub ok_host: OpalKellyHost, 9 | pub led: Signal>, 10 | pub local: Signal>, 11 | pub faders: [FaderWithSyncROM; 8], 12 | } 13 | 14 | impl Logic for OpalKellyWave { 15 | #[hdl_gen] 16 | fn update(&mut self) { 17 | OpalKellyHostInterface::link(&mut self.hi, &mut self.ok_host.hi); 18 | for i in 0_usize..8_usize { 19 | self.faders[i].clock.next = self.ok_host.ti_clk.val(); 20 | self.faders[i].enable.next = true; 21 | } 22 | self.local.next = 0x00.into(); 23 | for i in 0..8 { 24 | self.local.next = self 25 | .local 26 | .val() 27 | .replace_bit(i, !self.faders[i].active.val()); 28 | } 29 | self.led.next = self.local.val(); 30 | } 31 | } 32 | 33 | impl OpalKellyWave { 34 | pub fn new() -> Self { 35 | let faders: [FaderWithSyncROM; 8] = [ 36 | FaderWithSyncROM::new(MHZ48, 0), 37 | FaderWithSyncROM::new(MHZ48, 18), 38 | FaderWithSyncROM::new(MHZ48, 36), 39 | FaderWithSyncROM::new(MHZ48, 54), 40 | FaderWithSyncROM::new(MHZ48, 72), 41 | FaderWithSyncROM::new(MHZ48, 90), 42 | FaderWithSyncROM::new(MHZ48, 108), 43 | FaderWithSyncROM::new(MHZ48, 128), 44 | ]; 45 | Self { 46 | hi: B::hi(), 47 | ok_host: B::ok_host(), 48 | local: Signal::default(), 49 | faders, 50 | led: B::leds(), 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /rust-hdl-ok-frontpanel-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-hdl-ok-frontpanel-sys" 3 | version = "0.46.0" 4 | edition = "2018" 5 | license = "MIT" 6 | description = "OpalKelly FrontPanel library wrapper for the RustHDL crate." 7 | homepage = "https://rust-hdl.org" 8 | repository = "https://github.com/samitbasu/rust-hdl" 9 | keywords = ["fpga", "verilog", "hardware"] 10 | authors = ["Samit Basu "] 11 | 12 | 13 | links = "okFrontPanel" 14 | build = "build.rs" 15 | 16 | [dependencies] 17 | 18 | [build-dependencies] 19 | bindgen = "^0" 20 | -------------------------------------------------------------------------------- /rust-hdl-ok-frontpanel-sys/build.rs: -------------------------------------------------------------------------------- 1 | extern crate bindgen; 2 | 3 | use std::path::PathBuf; 4 | 5 | fn main() { 6 | println!("cargo:rustc-link-lib=dylib=okFrontPanel"); 7 | let root_path = PathBuf::from(std::env::var("OK_FRONTPANEL_DIR").expect( 8 | "Set OK_FRONTPANEL_DIR to absolute path of the FrontPanel/API directory on this system", 9 | )); 10 | println!("cargo:rustc-link-lib=dylib=okFrontPanel"); 11 | println!( 12 | "cargo:rustc-link-search=native={}", 13 | root_path.to_str().unwrap() 14 | ); 15 | 16 | let bindings = bindgen::Builder::default() 17 | .header(root_path.join("okFrontPanel.h").to_str().unwrap()) 18 | .generate() 19 | .expect("Unable to generate bindings"); 20 | 21 | let out_path = PathBuf::from(std::env::var("OUT_DIR").unwrap()); 22 | bindings 23 | .write_to_file(out_path.join("bindings.rs")) 24 | .expect("Couldn't write bindings!"); 25 | } 26 | -------------------------------------------------------------------------------- /rust-hdl-ok-frontpanel-sys/readme.md: -------------------------------------------------------------------------------- 1 | # Quickstart 2 | 3 | Set the `OK_FRONTPANEL_DIR` environment variable to point to the `FrontPanel/API` directory. This 4 | should be the location of the `okFrontPanel.h` header file. 5 | 6 | ## Mac OS X 7 | 8 | Set the `DYLD_FALLBACK_LIBRARY_PATH` environment variable to point to the same directory. You can do 9 | 10 | ```shell 11 | export DYLD_FALLBACK_LIBRARY_PATH=$OK_FRONTPANEL_DIR 12 | ``` 13 | 14 | Then the usual `cargo build` and `cargo test` should work. 15 | -------------------------------------------------------------------------------- /rust-hdl-org/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | -------------------------------------------------------------------------------- /rust-hdl-org/README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator. 4 | 5 | ### Installation 6 | 7 | ``` 8 | $ yarn 9 | ``` 10 | 11 | ### Local Development 12 | 13 | ``` 14 | $ yarn start 15 | ``` 16 | 17 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. 18 | 19 | ### Build 20 | 21 | ``` 22 | $ yarn build 23 | ``` 24 | 25 | This command generates static content into the `build` directory and can be served using any static contents hosting service. 26 | 27 | ### Deployment 28 | 29 | Using SSH: 30 | 31 | ``` 32 | $ USE_SSH=true yarn deploy 33 | ``` 34 | 35 | Not using SSH: 36 | 37 | ``` 38 | $ GIT_USER= yarn deploy 39 | ``` 40 | 41 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. 42 | -------------------------------------------------------------------------------- /rust-hdl-org/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /rust-hdl-org/docs/guide/fpga/cybersecurity.md: -------------------------------------------------------------------------------- 1 | # Cybersecurity 2 | 3 | Who doesn't worry about cybersecurity these days? CPU software ecosystems are extraordinarily 4 | complex. And it is becoming increasingly difficult to ensure a system cannot be tampered with. 5 | The flexible nature of the CPU means it can also be repurposed/subverted so that it does 6 | something you never intended. While it's an oversimplification to say that FPGAs don't suffer 7 | from the same problem, the "factory" nature of their construction certainly makes it more 8 | difficult. The spectre of undefined behavior can pop up in FPGA systems (particularly at the 9 | boundaries to the real world), but it's nothing like you see in software systems. FPGA designs 10 | tend to be built out of smaller, simpler components, strung together in complex topologies. They 11 | are harder to compromise. Or at least it feels that way. 12 | 13 | Great! I've either convinced you that an FPGA is a great solution to some problem 14 | you are wrestling with, or bored you to tears. Either way, let's look at the problem with 15 | programming FPGAs. 16 | -------------------------------------------------------------------------------- /rust-hdl-org/docs/guide/fpga/deterministic-systems.md: -------------------------------------------------------------------------------- 1 | # Deterministic systems 2 | 3 | Lets suppose you have to do some incredibly boring and repetitive task, 4 | like take the temperature of your Kombucha setup every so often. Yawn. 5 | Piece of cake, right? Fire up you PC, plug in a USB-based thermometer, write 6 | some Python code, and go have lunch (and look for your shoes). Hmmm, 7 | but I want to do this constantly - like 24/7. Oh. Ok - so an embedded 8 | solution. Like a Raspberry Pi! Simple enough (and often simple is the best solution). 9 | 10 | Let's make it a bit harder. Instead of reading the temperature every second, 11 | suppose we need to measure a _lot_ more frequently. Like 10 times a second. 12 | Or maybe 1000 times a second. Ok - so regular operating systems don't really like 13 | that kind of thing. They will offer you the ability to run a task periodically, but 14 | rarely guarantee that nothing will pre-empt your temperature measurement. How about 15 | a real time operating system (RTOS)? Sure! RTOSes can give you deterministic scheduling 16 | and guarantee that your process will execute when you want. But most have schedulers 17 | that are limited to kilohertz ranges. As we increase the speed with which we want 18 | to measure our Kombucha, we find that RTOSes kind of shrug and wave us on. 19 | 20 | Let's suppose you end up with an embedded solution that with enough wrangling 21 | and coercion you can get to run at 10KHz - so that you sample the temperature 22 | fast enough to make your brewmaster happy. Chances are, you aren't doing a whole 23 | lot more with your controller at this point. What if I need a second one measured 24 | at a different rate? Um. Ok - let's say the second one needs to measured at 25 | 20KHz. Why 20KHz? Well - because it immediately raises a problem. On every 26 | second sample, you will be trying to measure 2 temperatures simultaneously. How 27 | do you do that? In general, a CPU cannot. CPU cores typically manage a single 28 | thread of execution at a time. When you ask them to communicate with thermometer A 29 | at time T, and then also ask them to communicate with thermometer B at time T, 30 | the operating system will pick one of the two and then make the other one wait. 31 | -------------------------------------------------------------------------------- /rust-hdl-org/docs/guide/fpga/high-speed-io.md: -------------------------------------------------------------------------------- 1 | # High Speed I/O 2 | 3 | Another area where FPGAs excel is at high speed predictable I/O. Suppose you are 4 | building yourself a radio, and want to sample some intermediate signal at a couple 5 | of MSPS (Million samples per second). It happens. That means that some dedicated 6 | circuitry is going to be sending you data at some pretty high rate. How high? 7 | Well, assume the samples are 16 bits each, and you are shooting for 2 MSPS. That 8 | means you need about 32 Mbits/second to keep up with the incoming data. Sounds easy, 9 | right? I mean, we have ubiquitous 1 Gbit/second ethernet everywhere. What's a 10 | 32 Mbit signal stream? 11 | 12 | But it is actually not so easy. That chip (an analog digital converter chip) will send 13 | a constant stream of data at high speed. You might be able to read that with a microcontroller, 14 | but it won't be easy. Even if the chip satisfies some standard protocol, like SPI, 15 | it may not send data the way your hardware SPI controller expects. The chip manufacturer 16 | has probably assumed that this is Your Problem. 17 | 18 | FPGAs can solve these types of problems through a combination of 3 pieces: 19 | 20 | - They typically have dedicated circuitry that deals with high speed I/O, and most high 21 | speed interfaces operate the same or similar way. These circuits speak the various electrical 22 | protocols used by standards such as LVDS, etc. 23 | - They allow you to quickly pack high speed, narrow data streams, into lower speed, wide 24 | data streams. A standard technique is to take that 32 Mbit/second stream and turn it 25 | into a 2 million words/second stream, where the words are 16 bits wide. 26 | - They typically contain hardware to do the reverse too - to take wide, slow streams and 27 | convert them back into fast, narrow ones. 28 | -------------------------------------------------------------------------------- /rust-hdl-org/docs/guide/fpga/index.md: -------------------------------------------------------------------------------- 1 | # What is an FPGA anyway? (and who needs one?) 2 | 3 | That is a great question! And one I am not really planning to answer. There are some great 4 | resources available if you have never worked with an FPGA before. The short version is that an 5 | FPGA can be thought of as collection of digital logic (and analog) circuits that are packaged together 6 | and that can be reconfigured through software. This is important to understand, since there is a 7 | fundamental difference between FPGAs and the CPUs that have become ubiquitous and dominant in the 8 | electronics industry. FPGAs are fundamentally *massively parallel devices*. As such, they are 9 | not just really fast processors. In fact, in terms of raw clock speed, modern CPUs and 10 | (even some microcontrollers) will run circles around an FPGA. CPUs are available in ever increasing 11 | speeds and with ever more cores and capabilities. Need a multi-core microcontroller that runs 12 | at 500Mhz? No problem. CPUs clocked at 4GHz? Tons of high speed DRAM? Yup. System-on-a-chip 13 | that contains a bunch of nifty peripherals? Yeah - you can use those too. 14 | 15 | So why am I starting out with a list of reasons why you don't need an FPGA? Primarily because FPGAs 16 | are really good at certain tasks, but to use them effectively is quite difficult. RustHDL is my effort 17 | to make them less difficult to use, but it doesn't make the underlying complexity disappear. You may say 18 | "So what do FPGAs do well, then?". Great question! Glad I asked it. There are a few areas 19 | where FPGAs still hold an advantage over other digital solutions. 20 | 21 | - [Deterministic systems](./deterministic-systems.md)! 22 | - [True parallelism](./kitchen-analogy.md) 23 | - [High speed I/O](./high-speed-io.md) 24 | - [Low power designs](./low-power.md) 25 | - [Cybersecurity](./cybersecurity.md) 26 | 27 | Keep reading for more details! 28 | -------------------------------------------------------------------------------- /rust-hdl-org/docs/guide/fpga/low-power.md: -------------------------------------------------------------------------------- 1 | # Saving those Joules 2 | 3 | Can you doe a lot with those new 400MHz Cortex-M4 ARM microprocessors? Sure! Are there any downsides? 4 | Of course not! 5 | 6 | Well... OK. A few. Here are some things to think about 7 | 8 | - Check out how much power those processors use when doing nothing. 9 | - Double check that number. 10 | - Have a lie down. 11 | 12 | With an FPGA, in general, you pay only for what you use. You can usually clock it really slowly until 13 | something interesting happens, at which point, you wake up and do stuff. Or, you can just use parallelism and trade surface area for speed. Generally, power usage goes up with clock speed. Check 14 | out this [paper](https://www.mecs-press.org/ijieeb/ijieeb-v4-n5/IJIEEB-V4-N5-7.pdf). Cutting the clock speed in half means half the dynamic power usage (generally). 15 | 16 | A good example of this tradeoff is trying to do trig functions quickly. If you want to do e.g., 1 million cosines/sec for some reason, you probably need a hefty microcontroller. Something that is 17 | running at least 100 MHz or so, since a cosine is unlikely to take a single clock cycle through its 18 | APU and you have other things you need to do too. An FPGA, on the other hand, can compute a cosine 19 | in a single clock cycle (regardless of the speed) if it's sufficiently pipelined, and you have enough 20 | area. So that sets the speed at 1 MHz. That could be 1% of the power needed to keep the microcontroller doing the same work. Food for thought. -------------------------------------------------------------------------------- /rust-hdl-org/docs/guide/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 3 3 | --- 4 | # The Basics 5 | 6 | Let's start with the basic questions you may be asking yourself: 7 | 8 | - What is RustHDL? 9 | - Why do we need it? 10 | - What is an FPGA anyway! 11 | - Where are my shoes? 12 | 13 | These are all valid, and important questions, and I hope to answer at least a couple 14 | of them in the course of this guide (did you check outside the front door?). If none of 15 | these are interesting to you, move on. This guide is probably not meant for you. 16 | 17 | Still here? Great! This is meant to be helpful. 18 | 19 | The guide is broken down into sections. 20 | 21 | - [What is an FPGA](./fpga/index.md) has some musings on FPGAs and maybe some useful tools to 22 | explain them to your colleagues. 23 | - [Programming](./programming/index.md) has a few observations on different programming models for 24 | FPGAs and how RustHDL fits in 25 | - [Using RustHDL](./rusthdl/index.md) covers the details of using RustHDL to build firmware 26 | 27 | -------------------------------------------------------------------------------- /rust-hdl-org/docs/guide/programming/index.md: -------------------------------------------------------------------------------- 1 | # Programming models for FPGAs 2 | 3 | I'm no expert on the history of FPGA programming. However, I can point out some guideposts in the area of FPGA programming, and my experience with them to date. Your mileage may, of course, vary. 4 | 5 | - [Verilog](./verilog.md) - adapted from simulating circuits to describing them 6 | - [Lucid](./lucid.md) - an improvement on the ergonomics and DX for Verilog 7 | - [MyHDL/MiGen/Python](./migen.md) - Python based approaches to describing firmware -------------------------------------------------------------------------------- /rust-hdl-org/docs/guide/programming/lucid.md: -------------------------------------------------------------------------------- 1 | # Lucid 2 | 3 | Lucid is an excellent example of how Verilog can be improved. Developed by 4 | what is now Alchitry Labs, Lucid is a higher level language that can be translated 5 | into Verilog by the Alchitry IDE. Lucid introduces things like types, structs, 6 | and other concepts that we will encounter. These are standard 7 | programming concepts that we think about all the time, but which are typically 8 | absent in standard Verilog code. At least as of now (Nov 2021), Lucid is tied to the 9 | Alchitry IDE, and while I enjoy using Alchitry products, I also need to use FPGAs from 10 | other vendors and suppliers. 11 | -------------------------------------------------------------------------------- /rust-hdl-org/docs/guide/programming/verilog.md: -------------------------------------------------------------------------------- 1 | # Verilog 2 | 3 | Verilog was originally a language for simulating the behavior of digital circuits 4 | that over time, became repurposed for FPGA programming. FPGA programming is generally 5 | "structural", and not "behavioral". Going back to the kitchen analogy (again?? Is this 6 | guy permanently hungry?), an FPGA program is like a floor plan for a factory. It tells 7 | you what sections are set up to do what function, and tells you how they are connected 8 | to each other. It does not (necessarily) tell you how that system behaves. This is 9 | exactly unlike CPU programming, which is a series of directed instructions, like a recipe. 10 | In most software languages, you provide a series of instructions, or maybe you describe 11 | the desired outcome. In both cases, the CPU ends up with a stream of instructions telling 12 | it to chop this, dice that, and then add the rice. 13 | 14 | Verilog is the lingua franca of the FPGA world. And it is still the main way FPGAs are 15 | programmed. All of the newer technologies/languages ultimately generate Verilog or something 16 | equivalent to it. The toolchains, after all, expect Verilog as input. 17 | 18 | So are we done? Maybe! For many FPGA developers (particularly those with hardware backgrounds), 19 | Verilog or VHDL are the end of the story. Much the same way for many CPU programmers, assembly 20 | language or machine code are sufficient. End of story. For the rest of us, the goal is 21 | to make developing FPGA programs slightly less traumatizing. Compiler technology has come 22 | a long, long way in the past several decades. The Rust compiler (see! I hadn't forgot 23 | the purpose of this book) is incredibly sophisticated, and it goes through all kinds of hoops 24 | to make sure you don't do silly things. 25 | 26 | Verilog lacks those same safeties. It gives you, as the developer, ultimate power and 27 | flexibility, with no guide-rails. There are carefully constructed lists of rules regarding 28 | how to use Verilog in ways that give up much of its flexibility in favor of readability. 29 | -------------------------------------------------------------------------------- /rust-hdl-org/docs/guide/rusthdl/bits.md: -------------------------------------------------------------------------------- 1 | # Representing bits 2 | 3 | The [Bits](core::bits::Bits) type is a `Copy` enabled type that you can construct from integers, 4 | from the `Default` trait, or from other `Bits`. Mostly, it is meant to stay out of your way 5 | and behave like a `u32`. 6 | 7 | ```rust 8 | let x: Bits<50> = Default::default(); 9 | ``` 10 | This will construct a length 50 bit vector that is initialized to all `0`. 11 | 12 | You can also convert from literals into bit vectors using the [From] and [Into] traits, 13 | provided the literals are of the `u64` type. 14 | 15 | ```rust 16 | let x: Bits<50> = 0xBEEF.into(); 17 | ``` 18 | 19 | In some cases, Rust complains about literals, and you may need to provide a suffix: 20 | ```rust 21 | let x: Bits<50> = 0xDEAD_BEEF_u64.into(); 22 | ``` 23 | However, in most cases, you can leave literals suffix-free, and Rust will automatically 24 | determine the type from the context. 25 | 26 | You can construct a larger constant using the [bits] function. If you have a literal of up to 27 | 128 bits, it provides a functional form 28 | ```rust 29 | let x: Bits<200> = bits(0xDEAD_BEEE); // Works for up to 128 bit constants. 30 | ``` 31 | 32 | There is also the [ToBits] trait, which is implemented on the basic unsigned integer types. 33 | This trait allows you to handily convert from different integer values 34 | 35 | ```rust 36 | let x: Bits<10> = 32_u8.to_bits(); 37 | ``` 38 | 39 | -------------------------------------------------------------------------------- /rust-hdl-org/docs/guide/rusthdl/index.md: -------------------------------------------------------------------------------- 1 | # Using RustHDL 2 | 3 | This section of the documentation covers the details of using RustHDL to generate firmware. 4 | 5 | Here is an overview of the sections. 6 | 7 | - [bits](bits.md) - how to manage arbitrary bit-width signals 8 | - [operators](operators.md) - operations that are defined on signals 9 | - [signals](signals.md) - the struct used to represent a signal (wire) in the firmware 10 | - [traits](traits.md) - the traits used to implement RustHDL 11 | - [synthesizable](synthesizable.md) - the synthesizable subset of Rust 12 | - [interfaces](interfaces.md) - how to simplify your designs with interfaces 13 | - [simulation](simulation.md) - how to simulate your design in RustHDL 14 | - [high level synthesis](high_level_synthesis.md) - a high level synthesis library written in RustHDL 15 | - [loops](loops.md) - loops and arrays in RustHDL 16 | - [structs](struct_valued.md) - using struct-valued signals in RustHDL 17 | - [verilog](verilog.md) - generating Verilog from your RustHDL code 18 | - [wrapping](wrapping.md) - wrapping black box cores and other Verilog with your RustHDL code 19 | -------------------------------------------------------------------------------- /rust-hdl-org/docs/guide/rusthdl/operators.md: -------------------------------------------------------------------------------- 1 | # Operations on bits 2 | 3 | The [Bits](core::bits::Bits) type supports a subset of operations that can be synthesized in 4 | hardware. You can perform 5 | 6 | * Addition between `Bits` of the same size using the `+` operator 7 | * Subtraction between `Bits` of the same size using the `-` operator 8 | * Bitwise logical `AND` between `Bits` of the same size using the `&` operator 9 | * Bitwise logical `OR` between `Bits` of the same size using the `|` operator 10 | * Bitwise logical `XOR` (Exclusive Or) between `Bits` of the same size using the `^` operator 11 | * Bitwise comparisons for equality between `Bits` of the same size using `==` and `!=` operators 12 | * Unsigned comparisons (e.g., `>,>=,<,<=`) between `Bits` of the same size - these are 13 | always treated as unsigned values for comparison purposes. 14 | * Shift left using the `<<` operator 15 | * Shift right (no sign extension!) using the '>>' operator 16 | * Bitwise logical `NOT` using the `!` prefix operator 17 | 18 | These should feel natural when using RustHDL, as expressions follow Rust's rules (and not Verilog's). 19 | For example: 20 | ```rust 21 | # use rust_hdl::prelude::*; 22 | let x: Bits<32> = 0xDEAD_0000_u32.to_bits(); 23 | let y: Bits<32> = 0x0000_BEEF_u32.to_bits(); 24 | let z = x + y; 25 | assert_eq!(z, 0xDEAD_BEEF_u32.to_bits()); 26 | ``` 27 | 28 | You can, of course, construct expressions of arbitrary complexity using parenthesis, etc. 29 | The only real surprise may be at synthesis time, when you try to fit the expression onto hardware. 30 | -------------------------------------------------------------------------------- /rust-hdl-org/docs/img/16526-Alchitry_Cu_FPGA_Development_Board__Lattice_iCE40_HX_-03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samitbasu/rust-hdl/1d46ea9e82d2f66d76b41333f7ec4f66bdde122f/rust-hdl-org/docs/img/16526-Alchitry_Cu_FPGA_Development_Board__Lattice_iCE40_HX_-03.jpg -------------------------------------------------------------------------------- /rust-hdl-org/docs/references.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 100 3 | --- 4 | # References 5 | 6 | RustHDL was inspired by and borrows from the following awesome projects! They are listed in 7 | no particular order... 8 | 9 | - LucidHDL is a very cool HDL developed by the folks over at [Alchitry](https://www.alchitry.com). I initially 10 | started writing tooling to manipulate Lucid using Rust, and then realized that it would be better for 11 | me to just use Rust itself. But there is some great stuff on their website, and their book is excellent. 12 | - AlchitryLabs is an IDE from [Alchitry](https://www.alchitry.com) that supports the LucidHDL. They open sourced 13 | it and it is an impressive piece of software. 14 | - For Python fans, [MyHDL](https://myhdl.org) is a Python based approach to generating HDL. If you consider yourself a Python person, check it out. After I left Verilog, I first rewrote a fair chunk of firmware using MyHDL and Python. In the end, it wasn't for me, but many of the ideas are very cleanly expressed. 15 | - _The_ open source toolchain that started it all is [IceStorm](https://clifford.at/icestorm) Open Source tool chain for the `iCE 40` FPGA, and an incredibly powerful concept. In particular, Claire's work demonstrated a software-like path for handling FPGAs that was critical to a number of open source FPGA-centric projects. 16 | - [YoSys](https://github.com/YosysHQ/yosys) is the Verilog synthesis suite used by [RustHDL] to 17 | process generated Verilog and check a design for potential errors (static analysis). 18 | - [Icarus](http://iverilog.icarus.com/) is a verilog simulator that is software based, open source 19 | and easy to use. Before [RustHDL] got it's own simulator, I would write and simulate everything 20 | using [Icarus]. 21 | - [Verilator](https://www.veripool.org/verilator/) I'd condsider this the professional option 22 | for simulation (particularly of super complicated designs). [RustHDL] doesn't come anywhere near 23 | the simulation performance of [Verilator] yet, but the goal is to either generate Verilog that 24 | [Verilator] can process, or adopt the same types of techniques described by the author in this 25 | [paper](https://veripool.org/papers/Verilator_Internals1_202010.pdf). 26 | - [OpalKelly](https://opalkelly.com) - Excellent FPGA modules to use for pretty much any purpose. Their 27 | FrontPanel API is super easy to use, and RustHDL provides bindings to make it trivial. 28 | -------------------------------------------------------------------------------- /rust-hdl-org/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rust-hdl-org", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start", 8 | "build": "docusaurus build", 9 | "swizzle": "docusaurus swizzle", 10 | "deploy": "docusaurus deploy", 11 | "clear": "docusaurus clear", 12 | "serve": "docusaurus serve", 13 | "write-translations": "docusaurus write-translations", 14 | "write-heading-ids": "docusaurus write-heading-ids", 15 | "typecheck": "tsc" 16 | }, 17 | "dependencies": { 18 | "@docusaurus/core": "2.4.0", 19 | "@docusaurus/preset-classic": "2.4.0", 20 | "@mdx-js/react": "^1.6.22", 21 | "clsx": "^1.2.1", 22 | "prism-react-renderer": "^1.3.5", 23 | "react": "^17.0.2", 24 | "react-dom": "^17.0.2", 25 | "react-player": "^2.12.0" 26 | }, 27 | "devDependencies": { 28 | "@docusaurus/module-type-aliases": "2.4.0", 29 | "@tsconfig/docusaurus": "^1.0.5", 30 | "typescript": "^4.7.4" 31 | }, 32 | "browserslist": { 33 | "production": [ 34 | ">0.5%", 35 | "not dead", 36 | "not op_mini all" 37 | ], 38 | "development": [ 39 | "last 1 chrome version", 40 | "last 1 firefox version", 41 | "last 1 safari version" 42 | ] 43 | }, 44 | "engines": { 45 | "node": ">=16.14" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /rust-hdl-org/sidebars.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Creating a sidebar enables you to: 3 | - create an ordered group of docs 4 | - render a sidebar for each doc of that group 5 | - provide next/previous navigation 6 | 7 | The sidebars can be generated from the filesystem, or explicitly defined here. 8 | 9 | Create as many sidebars as you want. 10 | */ 11 | 12 | // @ts-check 13 | 14 | /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ 15 | const sidebars = { 16 | // By default, Docusaurus generates a sidebar from the docs folder structure 17 | tutorialSidebar: [{type: 'autogenerated', dirName: '.'}], 18 | 19 | // But you can create a sidebar manually 20 | /* 21 | tutorialSidebar: [ 22 | 'intro', 23 | 'hello', 24 | { 25 | type: 'category', 26 | label: 'Tutorial', 27 | items: ['tutorial-basics/create-a-document'], 28 | }, 29 | ], 30 | */ 31 | }; 32 | 33 | module.exports = sidebars; 34 | -------------------------------------------------------------------------------- /rust-hdl-org/src/components/HomepageFeatures/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import clsx from 'clsx'; 3 | import styles from './styles.module.css'; 4 | 5 | type FeatureItem = { 6 | title: string; 7 | Svg: React.ComponentType>; 8 | description: JSX.Element; 9 | }; 10 | 11 | const FeatureList: FeatureItem[] = [ 12 | { 13 | title: 'Safe', 14 | Svg: require('@site/static/img/cyber_ferris_safe.svg').default, 15 | description: ( 16 | <> 17 | Use rustc to check the validity of your firmware with 18 | strongly typed interfaces that are checked at compile time. 19 | 20 | ), 21 | }, 22 | { 23 | title: 'Powerful', 24 | Svg: require('@site/static/img/cyber_ferris_powerful.svg').default, 25 | description: ( 26 | <> 27 | Easily package complex designs into easy-to-reuse modules that 28 | you can reuse easily. Connecting components is simple and missing or erroneous 29 | connections are caught at compile time or with the built in static analysis passes. 30 | 31 | ), 32 | }, 33 | { 34 | title: 'Batteries Included', 35 | Svg: require('@site/static/img/cyber_ferris_batteries.svg').default, 36 | description: ( 37 | <> 38 | Need an asynchronous FIFO? Or a SDR memory controller? Or a one shot? 39 | Use the provided set of widgets to get started. Most are generic 40 | and can be used to handle arbitrary data types. 41 | 42 | ), 43 | }, 44 | ]; 45 | 46 | function Feature({ title, Svg, description }: FeatureItem) { 47 | return ( 48 |

49 |
50 | 51 |
52 |
53 |

{title}

54 |

{description}

55 |
56 |
57 | ); 58 | } 59 | 60 | export default function HomepageFeatures(): JSX.Element { 61 | return ( 62 |
63 |
64 |
65 | {FeatureList.map((props, idx) => ( 66 | 67 | ))} 68 |
69 |
70 |
71 | ); 72 | } 73 | -------------------------------------------------------------------------------- /rust-hdl-org/src/components/HomepageFeatures/styles.module.css: -------------------------------------------------------------------------------- 1 | .features { 2 | display: flex; 3 | align-items: center; 4 | padding: 2rem 0; 5 | width: 100%; 6 | } 7 | 8 | .featureSvg { 9 | height: 200px; 10 | width: 200px; 11 | } 12 | -------------------------------------------------------------------------------- /rust-hdl-org/src/css/custom.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Any CSS included here will be global. The classic template 3 | * bundles Infima by default. Infima is a CSS framework designed to 4 | * work well for content-centric websites. 5 | */ 6 | 7 | /* You can override the default Infima variables here. */ 8 | :root { 9 | --ifm-color-primary: #2e8555; 10 | --ifm-color-primary-dark: #29784c; 11 | --ifm-color-primary-darker: #277148; 12 | --ifm-color-primary-darkest: #205d3b; 13 | --ifm-color-primary-light: #33925d; 14 | --ifm-color-primary-lighter: #359962; 15 | --ifm-color-primary-lightest: #3cad6e; 16 | --ifm-code-font-size: 95%; 17 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); 18 | } 19 | 20 | /* For readability concerns, you should choose a lighter palette in dark mode. */ 21 | [data-theme='dark'] { 22 | --ifm-color-primary: #25c2a0; 23 | --ifm-color-primary-dark: #21af90; 24 | --ifm-color-primary-darker: #1fa588; 25 | --ifm-color-primary-darkest: #1a8870; 26 | --ifm-color-primary-light: #29d5b0; 27 | --ifm-color-primary-lighter: #32d8b4; 28 | --ifm-color-primary-lightest: #4fddbf; 29 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); 30 | } 31 | -------------------------------------------------------------------------------- /rust-hdl-org/src/pages/index.module.css: -------------------------------------------------------------------------------- 1 | /** 2 | * CSS files with the .module.css suffix will be treated as CSS modules 3 | * and scoped locally. 4 | */ 5 | 6 | .heroBanner { 7 | padding: 4rem 0; 8 | text-align: center; 9 | position: relative; 10 | overflow: hidden; 11 | } 12 | 13 | @media screen and (max-width: 996px) { 14 | .heroBanner { 15 | padding: 2rem; 16 | } 17 | } 18 | 19 | .buttons { 20 | display: flex; 21 | align-items: center; 22 | justify-content: center; 23 | } 24 | -------------------------------------------------------------------------------- /rust-hdl-org/src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import clsx from 'clsx'; 3 | import Link from '@docusaurus/Link'; 4 | import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; 5 | import Layout from '@theme/Layout'; 6 | import HomepageFeatures from '@site/src/components/HomepageFeatures'; 7 | 8 | import styles from './index.module.css'; 9 | 10 | function HomepageHeader() { 11 | const { siteConfig } = useDocusaurusContext(); 12 | return ( 13 |
14 |
15 |

{siteConfig.title}

16 |

{siteConfig.tagline}

17 |
18 | 21 | Alchitry Cu - Blinky Tutorial - 5min ⏱️ 22 | 23 |
24 |
25 |
26 | ); 27 | } 28 | 29 | export default function Home(): JSX.Element { 30 | const { siteConfig } = useDocusaurusContext(); 31 | return ( 32 | 35 | 36 |
37 | 38 |
39 |
40 | ); 41 | } 42 | -------------------------------------------------------------------------------- /rust-hdl-org/src/pages/markdown-page.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Markdown page example 3 | --- 4 | 5 | # Markdown page example 6 | 7 | You don't need React to write simple standalone pages. 8 | -------------------------------------------------------------------------------- /rust-hdl-org/static/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samitbasu/rust-hdl/1d46ea9e82d2f66d76b41333f7ec4f66bdde122f/rust-hdl-org/static/.nojekyll -------------------------------------------------------------------------------- /rust-hdl-org/static/img/blinky.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samitbasu/rust-hdl/1d46ea9e82d2f66d76b41333f7ec4f66bdde122f/rust-hdl-org/static/img/blinky.mp4 -------------------------------------------------------------------------------- /rust-hdl-org/static/img/cyber_ferris_power.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samitbasu/rust-hdl/1d46ea9e82d2f66d76b41333f7ec4f66bdde122f/rust-hdl-org/static/img/cyber_ferris_power.png -------------------------------------------------------------------------------- /rust-hdl-org/static/img/docusaurus-social-card.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samitbasu/rust-hdl/1d46ea9e82d2f66d76b41333f7ec4f66bdde122f/rust-hdl-org/static/img/docusaurus-social-card.jpg -------------------------------------------------------------------------------- /rust-hdl-org/static/img/docusaurus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samitbasu/rust-hdl/1d46ea9e82d2f66d76b41333f7ec4f66bdde122f/rust-hdl-org/static/img/docusaurus.png -------------------------------------------------------------------------------- /rust-hdl-org/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samitbasu/rust-hdl/1d46ea9e82d2f66d76b41333f7ec4f66bdde122f/rust-hdl-org/static/img/favicon.ico -------------------------------------------------------------------------------- /rust-hdl-org/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // This file is not used in compilation. It is here just for a nice editor experience. 3 | "extends": "@tsconfig/docusaurus/tsconfig.json", 4 | "compilerOptions": { 5 | "baseUrl": "." 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /rust-hdl-sim/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-hdl-sim" 3 | version = "0.46.0" 4 | edition = "2021" 5 | license = "MIT" 6 | description = "Write firmware for FPGAs in Rust - Simulation crate" 7 | homepage = "https://rust-hdl.org" 8 | repository = "https://github.com/samitbasu/rust-hdl" 9 | keywords = ["fpga", "verilog", "hardware"] 10 | authors = ["Samit Basu "] 11 | 12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 13 | 14 | [dependencies] 15 | rust-hdl-core = { version = "0.46.0", path = "../rust-hdl-core" } 16 | rust-hdl-widgets = { version = "0.46.0", path = "../rust-hdl-widgets" } 17 | array-init = { version = "2.0.0" } 18 | -------------------------------------------------------------------------------- /rust-hdl-sim/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod ad7193_sim; 2 | pub mod ads8688_sim; 3 | pub mod ads868x_sim; 4 | pub mod max31856_sim; 5 | pub mod muxed_ad7193_sim; 6 | pub mod muxed_ads868x_sim; 7 | pub mod muxed_max31856_sim; 8 | pub mod prelude; 9 | pub mod sdr_sdram; 10 | -------------------------------------------------------------------------------- /rust-hdl-sim/src/muxed_ad7193_sim.rs: -------------------------------------------------------------------------------- 1 | use super::ad7193_sim::{AD7193Config, AD7193Simulator}; 2 | use rust_hdl_core::prelude::*; 3 | use rust_hdl_widgets::prelude::*; 4 | 5 | #[derive(LogicBlock)] 6 | pub struct MuxedAD7193Simulators { 7 | // Input SPI bus 8 | pub wires: SPIWiresSlave, 9 | pub addr: Signal>, 10 | pub mux: MuxSlaves<8, 3>, 11 | pub clock: Signal, 12 | adcs: [AD7193Simulator; 8], 13 | } 14 | 15 | impl MuxedAD7193Simulators { 16 | pub fn new(config: AD7193Config) -> Self { 17 | Self { 18 | wires: Default::default(), 19 | mux: Default::default(), 20 | addr: Default::default(), 21 | clock: Default::default(), 22 | adcs: array_init::array_init(|_| AD7193Simulator::new(config)), 23 | } 24 | } 25 | } 26 | 27 | impl Logic for MuxedAD7193Simulators { 28 | #[hdl_gen] 29 | fn update(&mut self) { 30 | SPIWiresSlave::link(&mut self.wires, &mut self.mux.from_master); 31 | for i in 0..8 { 32 | self.adcs[i].clock.next = self.clock.val(); 33 | SPIWiresMaster::join(&mut self.mux.to_slaves[i], &mut self.adcs[i].wires); 34 | } 35 | self.mux.sel.next = self.addr.val(); 36 | } 37 | } 38 | 39 | #[test] 40 | fn test_mux_is_synthesizable() { 41 | let mut uut = MuxedAD7193Simulators::new(AD7193Config::hw()); 42 | uut.connect_all(); 43 | yosys_validate("mux_7193", &generate_verilog(&uut)).unwrap(); 44 | } 45 | -------------------------------------------------------------------------------- /rust-hdl-sim/src/muxed_ads868x_sim.rs: -------------------------------------------------------------------------------- 1 | use super::ads868x_sim::ADS868XSimulator; 2 | use rust_hdl_core::prelude::*; 3 | use rust_hdl_widgets::prelude::*; 4 | 5 | #[derive(LogicBlock)] 6 | pub struct MuxedADS868XSimulators { 7 | // Input SPI bus 8 | pub wires: SPIWiresSlave, 9 | pub addr: Signal>, 10 | pub mux: MuxSlaves, 11 | pub clock: Signal, 12 | adcs: [ADS868XSimulator; N], 13 | } 14 | 15 | impl MuxedADS868XSimulators { 16 | pub fn new(config: SPIConfig) -> Self { 17 | assert!(N <= 8); 18 | Self { 19 | wires: Default::default(), 20 | mux: Default::default(), 21 | addr: Default::default(), 22 | clock: Default::default(), 23 | adcs: array_init::array_init(|_| ADS868XSimulator::new(config)), 24 | } 25 | } 26 | } 27 | 28 | impl Logic for MuxedADS868XSimulators { 29 | #[hdl_gen] 30 | fn update(&mut self) { 31 | SPIWiresSlave::link(&mut self.wires, &mut self.mux.from_master); 32 | for i in 0..N { 33 | self.adcs[i].clock.next = self.clock.val(); 34 | SPIWiresMaster::join(&mut self.mux.to_slaves[i], &mut self.adcs[i].wires); 35 | } 36 | self.mux.sel.next = self.addr.val(); 37 | } 38 | } 39 | 40 | #[test] 41 | fn test_mux_is_synthesizable() { 42 | let mut uut: MuxedADS868XSimulators<8> = 43 | MuxedADS868XSimulators::new(ADS868XSimulator::spi_hw()); 44 | uut.connect_all(); 45 | yosys_validate("mux_8689", &generate_verilog(&uut)).unwrap(); 46 | } 47 | -------------------------------------------------------------------------------- /rust-hdl-sim/src/muxed_max31856_sim.rs: -------------------------------------------------------------------------------- 1 | use super::max31856_sim::MAX31856Simulator; 2 | use rust_hdl_core::prelude::*; 3 | use rust_hdl_widgets::prelude::*; 4 | 5 | #[derive(LogicBlock)] 6 | pub struct MuxedMAX31856Simulators { 7 | // Input SPI bus 8 | pub wires: SPIWiresSlave, 9 | pub mux: MuxSlaves<8, 3>, 10 | pub addr: Signal>, 11 | pub clock: Signal, 12 | adcs: Vec, 13 | } 14 | 15 | impl MuxedMAX31856Simulators { 16 | pub fn new(config: SPIConfig) -> Self { 17 | Self { 18 | wires: Default::default(), 19 | mux: Default::default(), 20 | addr: Default::default(), 21 | clock: Default::default(), 22 | adcs: (0..8).map(|_| MAX31856Simulator::new(config)).collect(), 23 | } 24 | } 25 | } 26 | 27 | impl Logic for MuxedMAX31856Simulators { 28 | #[hdl_gen] 29 | fn update(&mut self) { 30 | SPIWiresSlave::link(&mut self.wires, &mut self.mux.from_master); 31 | self.mux.sel.next = self.addr.val(); 32 | for i in 0..8 { 33 | self.adcs[i].clock.next = self.clock.val(); 34 | SPIWiresMaster::join(&mut self.mux.to_slaves[i], &mut self.adcs[i].wires); 35 | } 36 | } 37 | } 38 | 39 | #[test] 40 | fn test_mux_is_synthesizable() { 41 | use super::ad7193_sim::AD7193Config; 42 | let mut uut = MuxedMAX31856Simulators::new(AD7193Config::hw().spi); 43 | uut.connect_all(); 44 | yosys_validate("mux_31865", &generate_verilog(&uut)).unwrap(); 45 | } 46 | -------------------------------------------------------------------------------- /rust-hdl-sim/src/prelude.rs: -------------------------------------------------------------------------------- 1 | pub use super::ad7193_sim::*; 2 | pub use super::ads868x_sim::*; 3 | pub use super::max31856_sim::*; 4 | pub use super::max31856_sim::*; 5 | pub use super::muxed_ad7193_sim::*; 6 | pub use super::muxed_ads868x_sim::*; 7 | pub use crate::sdr_sdram::chip::SDRAMSimulator; 8 | -------------------------------------------------------------------------------- /rust-hdl-sim/src/sdr_sdram/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod bank; 2 | pub mod chip; 3 | -------------------------------------------------------------------------------- /rust-hdl-widgets/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-hdl-widgets" 3 | version = "0.46.0" 4 | edition = "2021" 5 | license = "MIT" 6 | description = "Write firmware for FPGAs in Rust - widget crate" 7 | homepage = "https://rust-hdl.org" 8 | repository = "https://github.com/samitbasu/rust-hdl" 9 | keywords = ["fpga", "verilog", "hardware"] 10 | authors = ["Samit Basu "] 11 | 12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 13 | 14 | [dependencies] 15 | rust-hdl-core = { version = "0.46.0", path = "../rust-hdl-core" } 16 | array-init = "2.0.0" 17 | 18 | [dev-dependencies] 19 | rand = "0.8" 20 | -------------------------------------------------------------------------------- /rust-hdl-widgets/src/accum.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl_core::prelude::*; 2 | 3 | use crate::dff::DFF; 4 | 5 | #[derive(Clone, Debug, LogicBlock)] 6 | pub struct Accum { 7 | pub clock: Signal, 8 | pub strobe_in: Signal, 9 | pub data_in: Signal>, 10 | pub strobe_out: Signal, 11 | pub data_out: Signal>, 12 | accum: DFF>, 13 | counter: DFF>, 14 | max_count: Constant>, 15 | } 16 | 17 | impl Logic for Accum { 18 | fn update(&mut self) { 19 | self.accum.clock.next = self.clock.val(); 20 | self.counter.clock.next = self.clock.val(); 21 | self.strobe_out.next = false; 22 | self.data_out.next = self.accum.q.val(); 23 | self.accum.d.next = self.accum.q.val(); 24 | if self.strobe_in.val() { 25 | self.accum.d.next = self.accum.q.val() + bit_cast::(self.data_in.val()); 26 | self.counter.d.next = self.counter.q.val() + 1; 27 | } 28 | if self.counter.q.val() == self.max_count.val() { 29 | self.strobe_out.next = true; 30 | self.counter.d.next = 0.into(); 31 | self.accum.d.next = 0.into(); 32 | } 33 | } 34 | } 35 | 36 | impl Accum { 37 | pub fn new(count: usize) -> Self { 38 | assert!(P >= clog2(count)); 39 | assert!(M >= N + P); 40 | Self { 41 | clock: Default::default(), 42 | strobe_in: Default::default(), 43 | data_in: Default::default(), 44 | strobe_out: Default::default(), 45 | accum: DFF::default(), 46 | counter: DFF::default(), 47 | max_count: Constant::new(count.to_bits()), 48 | data_out: Default::default(), 49 | } 50 | } 51 | } 52 | 53 | #[test] 54 | fn test_accum_synthesizes() { 55 | let _p = TopWrap::new(Accum::<32, 40, 6>::new(50)); 56 | } 57 | -------------------------------------------------------------------------------- /rust-hdl-widgets/src/auto_reset.rs: -------------------------------------------------------------------------------- 1 | use crate::dff::DFF; 2 | use rust_hdl_core::prelude::*; 3 | 4 | #[derive(Clone, Debug, LogicBlock, Default)] 5 | pub struct AutoReset { 6 | pub reset: Signal, 7 | pub clock: Signal, 8 | dff: DFF>, 9 | } 10 | 11 | impl Logic for AutoReset { 12 | #[hdl_gen] 13 | fn update(&mut self) { 14 | self.dff.clock.next = self.clock.val(); 15 | self.dff.d.next = self.dff.q.val(); 16 | self.reset.next = false.into(); 17 | if !self.dff.q.val().all() { 18 | self.dff.d.next = self.dff.q.val() + 1; 19 | self.reset.next = true.into(); 20 | } 21 | } 22 | } 23 | 24 | #[test] 25 | fn test_synch_reset_synchronizes() { 26 | let mut uut = AutoReset::default(); 27 | uut.connect_all(); 28 | yosys_validate("sync_reset", &generate_verilog(&uut)).unwrap(); 29 | } 30 | -------------------------------------------------------------------------------- /rust-hdl-widgets/src/edge_detector.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl_core::prelude::*; 2 | 3 | use crate::{dff::DFF, dff_setup}; 4 | 5 | #[derive(LogicBlock)] 6 | pub struct EdgeDetector { 7 | pub input_signal: Signal, 8 | pub edge_signal: Signal, 9 | pub clock: Signal, 10 | prev: DFF, 11 | current: DFF, 12 | is_rising: Constant, 13 | } 14 | 15 | impl EdgeDetector { 16 | pub fn new(is_rising: bool) -> Self { 17 | Self { 18 | input_signal: Default::default(), 19 | edge_signal: Default::default(), 20 | clock: Default::default(), 21 | prev: Default::default(), 22 | current: Default::default(), 23 | is_rising: Constant::new(is_rising), 24 | } 25 | } 26 | } 27 | 28 | impl Logic for EdgeDetector { 29 | #[hdl_gen] 30 | fn update(&mut self) { 31 | dff_setup!(self, clock, prev, current); 32 | self.prev.d.next = self.current.q.val(); 33 | self.current.d.next = self.is_rising.val() ^ self.input_signal.val(); 34 | self.edge_signal.next = !self.current.q.val() & self.prev.q.val(); 35 | } 36 | } 37 | 38 | #[test] 39 | fn test_edge_detector_synthesizes() { 40 | let mut uut = EdgeDetector::new(false); 41 | uut.connect_all(); 42 | yosys_validate("edge", &generate_verilog(&uut)).unwrap(); 43 | } 44 | -------------------------------------------------------------------------------- /rust-hdl-widgets/src/edge_ff.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl_core::prelude::*; 2 | use rust_hdl_core::timing::TimingInfo; 3 | 4 | #[derive(Clone, Debug, LogicBlock, Default)] 5 | pub struct EdgeDFF { 6 | pub d: Signal, 7 | pub q: Signal, 8 | pub clk: Signal, 9 | } 10 | 11 | impl EdgeDFF { 12 | pub fn new(init: T) -> EdgeDFF { 13 | Self { 14 | d: Signal::default(), 15 | q: Signal::new_with_default(init), 16 | clk: Signal::default(), 17 | } 18 | } 19 | } 20 | 21 | // TODO - make this specializable 22 | impl Logic for EdgeDFF { 23 | fn update(&mut self) { 24 | if self.clk.pos_edge() { 25 | self.q.next = self.d.val() 26 | } 27 | } 28 | fn connect(&mut self) { 29 | self.q.connect(); 30 | } 31 | fn hdl(&self) -> Verilog { 32 | Verilog::Custom(format!( 33 | "\ 34 | initial begin 35 | q = {:x}; 36 | end 37 | 38 | always @(posedge clk) q <= d;", 39 | self.q.verilog() 40 | )) 41 | } 42 | fn timing(&self) -> Vec { 43 | vec![TimingInfo { 44 | name: "edge_ff".to_string(), 45 | clock: "clk".to_string(), 46 | inputs: vec!["d".into()], 47 | outputs: vec!["q".into()], 48 | }] 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /rust-hdl-widgets/src/fifo/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod async_fifo; 2 | //pub mod bidirectional_bus; 3 | pub mod cross_fifo; 4 | pub mod fifo_expander_n; 5 | pub mod fifo_logic; 6 | pub mod fifo_reducer; 7 | pub mod fifo_reducer_n; 8 | pub mod fifo_register; 9 | pub mod sync_fifo; 10 | -------------------------------------------------------------------------------- /rust-hdl-widgets/src/i2c/i2c_bus.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl_core::prelude::*; 2 | 3 | use crate::open_drain::{OpenDrainDriver, OpenDrainReceiver}; 4 | 5 | #[derive(LogicInterface, Default)] 6 | #[join = "I2CBusReceiver"] 7 | pub struct I2CBusDriver { 8 | pub sda: OpenDrainDriver, 9 | pub scl: OpenDrainDriver, 10 | } 11 | 12 | #[derive(LogicInterface, Default)] 13 | #[join = "I2CBusDriver"] 14 | pub struct I2CBusReceiver { 15 | pub sda: OpenDrainReceiver, 16 | pub scl: OpenDrainReceiver, 17 | } 18 | -------------------------------------------------------------------------------- /rust-hdl-widgets/src/i2c/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod i2c_controller; 2 | pub mod i2c_driver; 3 | pub mod i2c_target; 4 | pub mod i2c_test_target; 5 | pub mod sim; 6 | pub mod i2c_bus; 7 | -------------------------------------------------------------------------------- /rust-hdl-widgets/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod accum; 2 | pub mod auto_reset; 3 | pub mod delay_line; 4 | pub mod dff; 5 | pub mod dff_with_init; 6 | pub mod edge_detector; 7 | pub mod edge_ff; 8 | pub mod fifo; 9 | pub mod i2c; 10 | pub mod mac_fir; 11 | pub mod open_drain; 12 | pub mod png; 13 | pub mod prelude; 14 | pub mod pulser; 15 | pub mod pwm; 16 | pub mod ramrom; 17 | pub mod registered_edge_tristate; 18 | pub mod sdram; 19 | pub mod shot; 20 | pub mod spi; 21 | pub mod strobe; 22 | pub mod synchronizer; 23 | //pub mod test_helpers; 24 | pub mod tristate; 25 | -------------------------------------------------------------------------------- /rust-hdl-widgets/src/open_drain.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl_core::prelude::*; 2 | 3 | #[derive(LogicBlock, Default)] 4 | pub struct OpenDrainBuffer { 5 | pub bus: Signal, 6 | pub control: OpenDrainReceiver, 7 | } 8 | 9 | impl Logic for OpenDrainBuffer { 10 | fn update(&mut self) { 11 | if self.control.drive_low.val() { 12 | self.bus.next = false; 13 | } 14 | self.control.line_state.next = self.bus.val(); 15 | self.bus 16 | .set_tristate_is_output(self.control.drive_low.val()); 17 | } 18 | 19 | fn connect(&mut self) { 20 | self.bus.connect(); 21 | self.control.line_state.connect(); 22 | } 23 | 24 | fn hdl(&self) -> Verilog { 25 | Verilog::Custom(format!( 26 | "\ 27 | assign bus = control$drive_low ? 0 : 1'bz; 28 | always @(*) control$line_state = bus;" 29 | )) 30 | } 31 | } 32 | 33 | #[test] 34 | fn test_opendrain_synthesizes() { 35 | let mut uut = OpenDrainBuffer::default(); 36 | uut.connect_all(); 37 | let vlog = generate_verilog(&uut); 38 | println!("{}", vlog); 39 | yosys_validate("open_drain", &vlog).unwrap() 40 | } 41 | 42 | #[derive(LogicInterface, Default)] 43 | #[join = "OpenDrainReceiver"] 44 | pub struct OpenDrainDriver { 45 | pub drive_low: Signal, 46 | pub line_state: Signal, 47 | } 48 | 49 | #[derive(LogicInterface, Default)] 50 | #[join = "OpenDrainDriver"] 51 | pub struct OpenDrainReceiver { 52 | pub drive_low: Signal, 53 | pub line_state: Signal, 54 | } 55 | -------------------------------------------------------------------------------- /rust-hdl-widgets/src/png/lfsr.rs: -------------------------------------------------------------------------------- 1 | use crate::{dff_setup, prelude::DFFWithInit}; 2 | use rust_hdl_core::prelude::*; 3 | 4 | // Adopted from Alchitry.com Lucid module `pn_gen` 5 | // This version does not provide seed setting. It generates a fixed sequence. 6 | #[derive(LogicBlock)] 7 | pub struct LFSRSimple { 8 | pub clock: Signal, 9 | pub strobe: Signal, 10 | pub num: Signal>, 11 | x: DFFWithInit>, 12 | y: DFFWithInit>, 13 | z: DFFWithInit>, 14 | w: DFFWithInit>, 15 | t: Signal>, 16 | } 17 | 18 | const SEED: u128 = 0x843233523a613966423b622562592c62; 19 | 20 | impl Default for LFSRSimple { 21 | fn default() -> Self { 22 | Self { 23 | clock: Default::default(), 24 | strobe: Default::default(), 25 | num: Default::default(), 26 | t: Default::default(), 27 | x: DFFWithInit::new((SEED & 0xFFFF_FFFF_u128).to_bits()), 28 | y: DFFWithInit::new(((SEED >> 32) & 0xFFFF_FFFF_u128).to_bits()), 29 | z: DFFWithInit::new(((SEED >> 64) & 0xFFFF_FFFF_u128).to_bits()), 30 | w: DFFWithInit::new(((SEED >> 96) & 0xFFFF_FFFF_u128).to_bits()), 31 | } 32 | } 33 | } 34 | 35 | impl Logic for LFSRSimple { 36 | #[hdl_gen] 37 | fn update(&mut self) { 38 | dff_setup!(self, clock, x, y, z, w); 39 | self.num.next = self.w.q.val(); 40 | self.t.next = self.x.q.val() ^ (self.x.q.val() << 11); 41 | if self.strobe.val() { 42 | self.x.d.next = self.y.q.val(); 43 | self.y.d.next = self.z.q.val(); 44 | self.z.d.next = self.w.q.val(); 45 | self.w.d.next = 46 | self.w.q.val() ^ (self.w.q.val() >> 19) ^ self.t.val() ^ (self.t.val() >> 8); 47 | } 48 | } 49 | } 50 | 51 | #[test] 52 | fn test_lfsr_simple_synthesizes() { 53 | let mut uut = LFSRSimple::default(); 54 | uut.connect_all(); 55 | yosys_validate("lfsr", &generate_verilog(&uut)).unwrap(); 56 | } 57 | -------------------------------------------------------------------------------- /rust-hdl-widgets/src/png/mod.rs: -------------------------------------------------------------------------------- 1 | // Adopted from Alchitry.com Lucid module `pn_gen` 2 | pub mod lfsr; 3 | -------------------------------------------------------------------------------- /rust-hdl-widgets/src/pwm.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl_core::prelude::*; 2 | 3 | use crate::dff::DFF; 4 | 5 | #[derive(LogicBlock)] 6 | pub struct PulseWidthModulator { 7 | pub enable: Signal, 8 | pub threshold: Signal>, 9 | pub clock: Signal, 10 | pub active: Signal, 11 | counter: DFF>, 12 | } 13 | 14 | impl Default for PulseWidthModulator { 15 | fn default() -> Self { 16 | Self { 17 | enable: Signal::default(), 18 | threshold: Signal::default(), 19 | clock: Signal::default(), 20 | active: Signal::new_with_default(false), 21 | counter: Default::default(), 22 | } 23 | } 24 | } 25 | 26 | impl Logic for PulseWidthModulator { 27 | #[hdl_gen] 28 | fn update(&mut self) { 29 | clock!(self, clock, counter); 30 | self.counter.d.next = self.counter.q.val() + 1; 31 | self.active.next = self.enable.val() & (self.counter.q.val() < self.threshold.val()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /rust-hdl-widgets/src/ramrom/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod ram; 2 | pub mod rom; 3 | pub mod sync_rom; 4 | -------------------------------------------------------------------------------- /rust-hdl-widgets/src/ramrom/rom.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl_core::prelude::*; 2 | use std::collections::BTreeMap; 3 | 4 | #[derive(LogicBlock)] 5 | pub struct ROM { 6 | pub address: Signal>, 7 | pub data: Signal, 8 | _sim: Box, D>>, 9 | } 10 | 11 | impl ROM { 12 | pub fn new(values: BTreeMap, D>) -> Self { 13 | Self { 14 | address: Signal::default(), 15 | data: Signal::new_with_default(D::default()), 16 | _sim: Box::new(values), 17 | } 18 | } 19 | } 20 | 21 | pub fn make_btree_from_iterable, D: Synth, const N: usize>( 22 | v: I, 23 | ) -> BTreeMap, D> { 24 | let mut values = BTreeMap::new(); 25 | for (index, val) in v.enumerate() { 26 | let address: Bits = index.to_bits(); 27 | values.insert(address, val); 28 | } 29 | values 30 | } 31 | 32 | impl, D: Synth, const N: usize> From for ROM { 33 | fn from(v: I) -> Self { 34 | Self::new(make_btree_from_iterable(v)) 35 | } 36 | } 37 | 38 | impl Logic for ROM { 39 | fn update(&mut self) { 40 | self.data.next = *self._sim.get(&self.address.val()).unwrap_or(&D::default()); 41 | } 42 | 43 | fn connect(&mut self) { 44 | self.data.connect(); 45 | } 46 | 47 | fn hdl(&self) -> Verilog { 48 | let cases = self 49 | ._sim 50 | .iter() 51 | .map(|x| { 52 | format!( 53 | " {}: data = {};", 54 | x.0.verilog().to_string(), 55 | x.1.verilog().to_string() 56 | ) 57 | }) 58 | .collect::>() 59 | .join("\n"); 60 | Verilog::Custom(format!( 61 | "\ 62 | always @* 63 | case (address) 64 | {cases} 65 | default: data = {default}; 66 | endcase 67 | ", 68 | cases = cases, 69 | default = D::default().verilog().to_string() 70 | )) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /rust-hdl-widgets/src/ramrom/sync_rom.rs: -------------------------------------------------------------------------------- 1 | use crate::ramrom::rom::make_btree_from_iterable; 2 | use rust_hdl_core::prelude::*; 3 | use rust_hdl_core::timing::TimingInfo; 4 | use std::collections::BTreeMap; 5 | 6 | #[derive(LogicBlock)] 7 | pub struct SyncROM { 8 | pub address: Signal>, 9 | pub clock: Signal, 10 | pub data: Signal, 11 | _sim: Box, D>>, 12 | } 13 | 14 | impl SyncROM { 15 | pub fn new(values: BTreeMap, D>) -> Self { 16 | Self { 17 | address: Signal::default(), 18 | data: Signal::new_with_default(D::default()), 19 | clock: Signal::default(), 20 | _sim: Box::new(values), 21 | } 22 | } 23 | } 24 | 25 | impl, D: Synth, const N: usize> From for SyncROM { 26 | fn from(v: I) -> Self { 27 | Self::new(make_btree_from_iterable(v)) 28 | } 29 | } 30 | 31 | impl Logic for SyncROM { 32 | fn update(&mut self) { 33 | if self.clock.pos_edge() { 34 | self.data.next = *self._sim.get(&self.address.val()).unwrap_or(&D::default()); 35 | } 36 | } 37 | 38 | fn connect(&mut self) { 39 | self.data.connect(); 40 | } 41 | 42 | fn hdl(&self) -> Verilog { 43 | let init = self 44 | ._sim 45 | .iter() 46 | .map(|x| { 47 | format!( 48 | "mem[{}] = {}", 49 | x.0.verilog().to_string(), 50 | x.1.verilog().to_string() 51 | ) 52 | }) 53 | .collect::>() 54 | .join(";\n"); 55 | Verilog::Custom(format!( 56 | "\ 57 | reg[{D}:0] mem [{Acount}:0]; 58 | 59 | initial begin 60 | {init}; 61 | end 62 | 63 | always @(posedge clock) begin 64 | data <= mem[address]; 65 | end", 66 | D = D::BITS - 1, 67 | Acount = (1 << N) - 1, 68 | init = init 69 | )) 70 | } 71 | fn timing(&self) -> Vec { 72 | vec![TimingInfo { 73 | name: "sync_rom".to_string(), 74 | clock: "clock".to_string(), 75 | inputs: vec!["address".to_string()], 76 | outputs: vec!["data".to_string()], 77 | }] 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /rust-hdl-widgets/src/registered_edge_tristate.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl_core::prelude::*; 2 | 3 | use crate::{dff::DFF, dff_setup}; 4 | 5 | #[derive(LogicBlock, Default)] 6 | pub struct RegisteredEdgeTristate { 7 | pub bus: Signal>, 8 | pub write_enable: Signal, 9 | pub write_data: Signal>, 10 | pub read_data: Signal>, 11 | pub clock: Signal, 12 | dff_out: DFF>, 13 | dff_in: DFF>, 14 | } 15 | 16 | impl Logic for RegisteredEdgeTristate { 17 | fn update(&mut self) { 18 | dff_setup!(self, clock, dff_out, dff_in); 19 | if self.write_enable.val() { 20 | self.bus.next = self.dff_out.q.val(); 21 | } 22 | self.dff_in.d.next = self.bus.val(); 23 | self.read_data.next = self.dff_in.q.val(); 24 | self.bus.set_tristate_is_output(self.write_enable.val()); 25 | self.dff_out.d.next = self.write_data.val(); 26 | } 27 | fn connect(&mut self) { 28 | self.dff_out.clock.connect(); 29 | self.dff_in.clock.connect(); 30 | self.dff_in.d.connect(); 31 | self.dff_out.d.connect(); 32 | self.bus.connect(); 33 | self.read_data.connect(); 34 | } 35 | fn hdl(&self) -> Verilog { 36 | Verilog::Wrapper(Wrapper { 37 | code: format!( 38 | r#" 39 | 40 | reg [{WIDTH}:0] dff_in; 41 | reg [{WIDTH}:0] dff_out; 42 | assign bus = write_enable ? dff_out : {WIDTH}'bz; 43 | assign read_data = dff_in; 44 | always @(posedge clock) begin 45 | dff_in <= bus; 46 | end 47 | always @(posedge clock) begin 48 | dff_out <= write_data; 49 | end 50 | "#, 51 | WIDTH = W - 1 52 | ), 53 | cores: r#""#.to_string(), 54 | }) 55 | } 56 | } 57 | 58 | #[test] 59 | fn test_tristate_edge_synthesizes() { 60 | let mut uut = RegisteredEdgeTristate::<8>::default(); 61 | uut.connect_all(); 62 | let vlog = generate_verilog(&uut); 63 | yosys_validate("tristate_reg", &vlog).unwrap() 64 | } 65 | -------------------------------------------------------------------------------- /rust-hdl-widgets/src/sdram/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod basic_controller; 2 | pub mod buffer; 3 | pub mod burst_controller; 4 | pub mod cmd; 5 | pub mod fifo_sdram; 6 | pub mod timings; 7 | 8 | use rust_hdl_core::prelude::*; 9 | 10 | #[derive(Clone, Copy, Debug, PartialEq)] 11 | pub enum OutputBuffer { 12 | Wired, 13 | DelayOne, 14 | DelayTwo, 15 | } 16 | 17 | #[derive(LogicInterface, Clone, Debug, Default)] 18 | #[join = "SDRAMDevice"] 19 | pub struct SDRAMDriver { 20 | pub clk: Signal, 21 | pub we_not: Signal, 22 | pub cas_not: Signal, 23 | pub ras_not: Signal, 24 | pub cs_not: Signal, 25 | pub bank: Signal>, 26 | pub address: Signal>, 27 | pub write_data: Signal>, 28 | pub read_data: Signal>, 29 | pub write_enable: Signal, 30 | } 31 | 32 | #[derive(LogicInterface, Clone, Debug, Default)] 33 | #[join = "SDRAMDriver"] 34 | pub struct SDRAMDevice { 35 | pub clk: Signal, 36 | pub we_not: Signal, 37 | pub cas_not: Signal, 38 | pub ras_not: Signal, 39 | pub cs_not: Signal, 40 | pub bank: Signal>, 41 | pub address: Signal>, 42 | pub write_data: Signal>, 43 | pub read_data: Signal>, 44 | pub write_enable: Signal, 45 | } 46 | -------------------------------------------------------------------------------- /rust-hdl-widgets/src/shot.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl_core::prelude::*; 2 | use std::time::Duration; 3 | 4 | use crate::{dff::DFF, dff_setup}; 5 | 6 | #[derive(Clone, Debug, LogicBlock)] 7 | pub struct Shot { 8 | pub trigger: Signal, 9 | pub active: Signal, 10 | pub clock: Signal, 11 | pub fired: Signal, 12 | duration: Constant>, 13 | counter: DFF>, 14 | state: DFF, 15 | } 16 | 17 | impl Shot { 18 | pub fn new(frequency: u64, duration: Duration) -> Self { 19 | let duration_nanos = duration.as_nanos() as f64 * NANOS_PER_FEMTO; // duration in femtos 20 | let clock_period_nanos = freq_hz_to_period_femto(frequency as f64); 21 | let clocks = (duration_nanos / clock_period_nanos).floor() as u64; 22 | assert!(clocks < (1_u64 << N)); 23 | Self { 24 | trigger: Signal::default(), 25 | active: Signal::new_with_default(false), 26 | clock: Signal::default(), 27 | fired: Default::default(), 28 | duration: Constant::new(clocks.into()), 29 | counter: Default::default(), 30 | state: Default::default(), 31 | } 32 | } 33 | } 34 | 35 | impl Logic for Shot { 36 | #[hdl_gen] 37 | fn update(&mut self) { 38 | dff_setup!(self, clock, counter, state); 39 | if self.state.q.val() { 40 | self.counter.d.next = self.counter.q.val() + 1; 41 | } 42 | self.fired.next = false; 43 | if self.state.q.val() && (self.counter.q.val() == self.duration.val()) { 44 | self.state.d.next = false; 45 | self.fired.next = true; 46 | } 47 | self.active.next = self.state.q.val(); 48 | if self.trigger.val() { 49 | self.state.d.next = true; 50 | self.counter.d.next = 0.into(); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /rust-hdl-widgets/src/spi/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod master; 2 | pub mod master_dynamic_mode; 3 | pub mod mux; 4 | pub mod slave; 5 | -------------------------------------------------------------------------------- /rust-hdl-x-macro-core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-hdl-x-macro-core" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | anyhow = "1.0.72" 10 | colored-diff = "0.2.3" 11 | prettyplease = "0.2.12" 12 | proc-macro2 = "1.0.66" 13 | quote = "1.0.31" 14 | syn = { version = "2.0.26", features = ["full"] } 15 | -------------------------------------------------------------------------------- /rust-hdl-x-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-hdl-x-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | [lib] 8 | proc-macro = true 9 | 10 | [dependencies] 11 | proc-macro2 = "1.0.66" 12 | rust-hdl-x-macro-core = { version = "0.1.0", path = "../rust-hdl-x-macro-core" } 13 | -------------------------------------------------------------------------------- /rust-hdl-x-macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | 3 | #[proc_macro_derive(Loggable)] 4 | pub fn loggable(input: TokenStream) -> TokenStream { 5 | rust_hdl_x_macro_core::derive_loggable(input.into()) 6 | .unwrap() 7 | .into() 8 | } 9 | -------------------------------------------------------------------------------- /rust-hdl-x-widgets/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-hdl-x-widgets" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | rust-hdl-x = { path = "../rust-hdl-x" } 10 | rust-hdl = { path = "../rust-hdl" } 11 | -------------------------------------------------------------------------------- /rust-hdl-x-widgets/src/counter.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl::prelude::Bits; 2 | use rust_hdl_x::{synchronous::Synchronous, LogBuilder, Logger, TagID}; 3 | 4 | struct BitCounter { 5 | tag_input: TagID, 6 | tag_output: TagID>, 7 | } 8 | 9 | impl BitCounter { 10 | pub fn new(mut builder: impl LogBuilder) -> Self { 11 | let tag_input = builder.tag("input"); 12 | let tag_output = builder.tag("output"); 13 | Self { 14 | tag_input, 15 | tag_output, 16 | } 17 | } 18 | } 19 | 20 | impl Synchronous for BitCounter { 21 | type State = Bits; 22 | type Input = bool; 23 | type Output = Bits; 24 | 25 | fn compute( 26 | &self, 27 | mut logger: impl Logger, 28 | input: Self::Input, 29 | state: Self::State, 30 | ) -> (Self::Output, Self::State) { 31 | logger.log(self.tag_input, input); 32 | let new_state = if input { state + 1 } else { state }; 33 | let output = new_state; 34 | logger.log(self.tag_output, output); 35 | (output, new_state) 36 | } 37 | } 38 | 39 | #[cfg(test)] 40 | mod tests { 41 | use rust_hdl::prelude::freq_hz_to_period_femto; 42 | 43 | use super::*; 44 | #[test] 45 | fn test_counter_with_bits_argument() { 46 | let mut logger_builder = rust_hdl_x::basic_logger_builder::BasicLoggerBuilder::default(); 47 | let clock_period = freq_hz_to_period_femto(1e6) as u64; 48 | logger_builder.add_simple_clock(clock_period); 49 | let counter: BitCounter<24> = BitCounter::new(&mut logger_builder); 50 | let logger = logger_builder.build(); 51 | let mut last_output: Bits<24> = Default::default(); 52 | rust_hdl_x::single_clock_simulation( 53 | logger, 54 | counter, 55 | clock_period, 56 | 100_000_000, 57 | |cycle, output| { 58 | last_output = output; 59 | cycle % 2 == 0 60 | }, 61 | ); 62 | assert_eq!(last_output, (100_000_000 / 2) & 0xFF_FFFF); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /rust-hdl-x/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-hdl-x" 3 | version = "0.1.0" 4 | edition = "2021" 5 | description = "Experiments in RustHDL design and concepts" 6 | authors = ["Samit Basu "] 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | anyhow = "1.0.72" 12 | bincode = "1.3.3" 13 | colored-diff = "0.2.3" 14 | indexmap = "2.0.0" 15 | num-traits = "0.2.15" 16 | proc-macro2 = "1.0.66" 17 | quote = "1.0.31" 18 | rust-hdl = { version = "0.46.0", path = "../rust-hdl" } 19 | rust-hdl-x-macro = { version = "0.1.0", path = "../rust-hdl-x-macro" } 20 | serde = { version = "1.0.171", features = ["derive"] } 21 | serde_json = "1.0.105" 22 | syn = { version = "2.0.26", features = ["full"] } 23 | vcd = "0.7.0" 24 | -------------------------------------------------------------------------------- /rust-hdl-x/counter.bcd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samitbasu/rust-hdl/1d46ea9e82d2f66d76b41333f7ec4f66bdde122f/rust-hdl-x/counter.bcd -------------------------------------------------------------------------------- /rust-hdl-x/src/kind.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Clone, PartialEq)] 2 | pub enum SynthKind { 3 | Array { base: Box, size: usize }, 4 | Tuple { elements: Vec }, 5 | Struct { fields: Vec<(String, SynthKind)> }, 6 | Enum { variants: Vec<(String, SynthKind)> }, 7 | Bits { digits: usize }, 8 | Empty, 9 | } 10 | 11 | pub const fn clog2(t: usize) -> usize { 12 | let mut p = 0; 13 | let mut b = 1; 14 | while b < t { 15 | p += 1; 16 | b *= 2; 17 | } 18 | p 19 | } 20 | 21 | impl SynthKind { 22 | pub fn bits(&self) -> usize { 23 | match self { 24 | SynthKind::Array { base, size } => base.bits() * size, 25 | SynthKind::Tuple { elements } => elements.iter().map(|x| x.bits()).sum(), 26 | SynthKind::Struct { fields } => fields.iter().map(|x| x.1.bits()).sum(), 27 | SynthKind::Enum { variants } => { 28 | clog2(variants.len()) + variants.iter().map(|x| x.1.bits()).max().unwrap_or(0) 29 | } 30 | SynthKind::Bits { digits } => *digits, 31 | SynthKind::Empty => 0, 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /rust-hdl-x/src/loggable.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl::prelude::Bits; 2 | 3 | use crate::{ 4 | log::{LogBuilder, TagID}, 5 | logger::Logger, 6 | }; 7 | 8 | pub trait Loggable: Sized + Copy + Clone + Default + PartialEq { 9 | fn allocate(tag: TagID, builder: impl LogBuilder); 10 | fn record(&self, tag: TagID, logger: impl Logger); 11 | } 12 | 13 | impl Loggable for bool { 14 | fn allocate(tag: TagID, builder: impl LogBuilder) { 15 | builder.allocate(tag, 1); 16 | } 17 | 18 | fn record(&self, tag: TagID, mut logger: impl Logger) { 19 | logger.write_bool(tag, *self); 20 | } 21 | } 22 | 23 | impl Loggable for u8 { 24 | fn allocate(tag: TagID, builder: impl LogBuilder) { 25 | builder.allocate(tag, 8); 26 | } 27 | 28 | fn record(&self, tag: TagID, mut logger: impl Logger) { 29 | logger.write_small(tag, *self as u64); 30 | } 31 | } 32 | 33 | impl Loggable for u16 { 34 | fn allocate(tag: TagID, builder: impl LogBuilder) { 35 | builder.allocate(tag, 16); 36 | } 37 | 38 | fn record(&self, tag: TagID, mut logger: impl Logger) { 39 | logger.write_small(tag, *self as u64); 40 | } 41 | } 42 | 43 | impl Loggable for u32 { 44 | fn allocate(tag: TagID, builder: impl LogBuilder) { 45 | builder.allocate(tag, 32); 46 | } 47 | 48 | fn record(&self, tag: TagID, mut logger: impl Logger) { 49 | logger.write_small(tag, *self as u64); 50 | } 51 | } 52 | 53 | impl Loggable for Bits { 54 | fn allocate(tag: TagID, builder: impl LogBuilder) { 55 | builder.allocate(tag, N); 56 | } 57 | 58 | fn record(&self, tag: TagID, mut logger: impl Logger) { 59 | match self { 60 | Bits::Short(x) => logger.write_small(tag, x.short() as u64), 61 | Bits::Long(x) => logger.write_large(tag, &x.bits()), 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /rust-hdl-x/src/logger.rs: -------------------------------------------------------------------------------- 1 | use crate::{log::TagID, loggable::Loggable}; 2 | 3 | pub trait Logger: Sized { 4 | fn set_time_in_fs(&mut self, time: u64); 5 | fn log(&mut self, tag: TagID, val: L) { 6 | val.record(tag, self) 7 | } 8 | fn write_bool(&mut self, tag: TagID, val: bool); 9 | fn write_small(&mut self, tag: TagID, val: u64); 10 | fn write_large(&mut self, tag: TagID, val: &[bool]); 11 | fn write_string(&mut self, tag: TagID, val: &'static str); 12 | } 13 | 14 | impl Logger for &mut T { 15 | fn set_time_in_fs(&mut self, time: u64) { 16 | (**self).set_time_in_fs(time) 17 | } 18 | fn write_bool(&mut self, tag: TagID, val: bool) { 19 | (**self).write_bool(tag, val) 20 | } 21 | 22 | fn write_small(&mut self, tag: TagID, val: u64) { 23 | (**self).write_small(tag, val) 24 | } 25 | 26 | fn write_large(&mut self, tag: TagID, val: &[bool]) { 27 | (**self).write_large(tag, val) 28 | } 29 | 30 | fn write_string(&mut self, tag: TagID, val: &'static str) { 31 | (**self).write_string(tag, val) 32 | } 33 | } 34 | 35 | impl Logger for () { 36 | fn set_time_in_fs(&mut self, _: u64) {} 37 | 38 | fn write_bool(&mut self, _: TagID, _: bool) {} 39 | 40 | fn write_small(&mut self, _: TagID, _: u64) {} 41 | 42 | fn write_large(&mut self, _: TagID, _: &[bool]) {} 43 | 44 | fn write_string(&mut self, _: TagID, _: &'static str) {} 45 | } 46 | -------------------------------------------------------------------------------- /rust-hdl-x/src/synchronous.rs: -------------------------------------------------------------------------------- 1 | use crate::{loggable::Loggable, logger::Logger}; 2 | 3 | pub trait Synchronous: Sized { 4 | type Input: Copy + Loggable + PartialEq; 5 | type Output: Copy + Loggable + Default; 6 | type State: Copy + Default + Loggable; 7 | // User provided 8 | fn compute( 9 | &self, 10 | logger: impl Logger, 11 | inputs: Self::Input, 12 | state: Self::State, 13 | ) -> (Self::Output, Self::State); 14 | } 15 | -------------------------------------------------------------------------------- /rust-hdl/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | *.dot 3 | -------------------------------------------------------------------------------- /rust-hdl/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-hdl" 3 | version = "0.46.0" 4 | edition = "2021" 5 | license = "MIT" 6 | description = "Write firmware for FPGAs in Rust" 7 | homepage = "https://rust-hdl.org" 8 | repository = "https://github.com/samitbasu/rust-hdl" 9 | keywords = ["fpga", "verilog", "hardware"] 10 | authors = ["Samit Basu "] 11 | 12 | [dependencies] 13 | rust-hdl-macros = { version = "0.46.0", path = "../rust-hdl-macros" } 14 | rust-hdl-core = { version = "0.46.0", path = "../rust-hdl-core" } 15 | rust-hdl-sim = { version = "0.46.0", path = "../rust-hdl-sim" } 16 | rust-hdl-hls = { version = "0.46.0", path = "../rust-hdl-hls" } 17 | rust-hdl-widgets = { version = "0.46.0", path = "../rust-hdl-widgets" } 18 | rust-hdl-fpga-support = { version = "0.46.0", path = "../rust-hdl-fpga-support", optional = true } 19 | crossbeam = "0.8.1" 20 | num-bigint = "0.4.0" 21 | num-traits = "0.2.14" 22 | vcd = "0.6.1" 23 | evalexpr = "6.3.0" 24 | regex = "1.5.4" 25 | array-init = "2.0.0" 26 | rand = "0.8" 27 | petgraph = "0.6.0" 28 | embed-doc-image = "0.1.4" 29 | svg = "0.18.0" 30 | substring = "^1" 31 | anyhow = "^1" 32 | 33 | seq-macro = "0.3.1" 34 | 35 | [features] 36 | fpga = ["dep:rust-hdl-fpga-support"] 37 | -------------------------------------------------------------------------------- /rust-hdl/doc/md/banner.md: -------------------------------------------------------------------------------- 1 | Write FPGA Firmware using Rust 2 | 3 | `rust-hdl` is a crate that allows you to write FPGA firmware using Rust! 4 | Specifically, `rust-hdl` compiles a subset of Rust down to Verilog so that 5 | you can synthesize firmware for your FPGA using standard tools. It also 6 | provides tools for simulation, verification, and analysis along with strongly 7 | typed interfaces for making sure your design works before heading to the bench. 8 | -------------------------------------------------------------------------------- /rust-hdl/doc/md/bits.md: -------------------------------------------------------------------------------- 1 | 2 | The [Bits](core::bits::Bits) type is a `Copy` enabled type that you can construct from integers, 3 | from the `Default` trait, or from other `Bits`. Mostly, it is meant to stay out of your way 4 | and behave like a `u32`. 5 | 6 | ``` 7 | # use rust_hdl::prelude::*; 8 | let x: Bits<50> = Default::default(); 9 | ``` 10 | This will construct a length 50 bit vector that is initialized to all `0`. 11 | 12 | You can also convert from literals into bit vectors using the [From] and [Into] traits, 13 | provided the literals are of the `u64` type. 14 | 15 | ``` 16 | # use rust_hdl::prelude::*; 17 | let x: Bits<50> = 0xBEEF.into(); 18 | ``` 19 | 20 | In some cases, Rust complains about literals, and you may need to provide a suffix: 21 | ``` 22 | # use rust_hdl::prelude::*; 23 | let x: Bits<50> = 0xDEAD_BEEF_u64.into(); 24 | ``` 25 | However, in most cases, you can leave literals suffix-free, and Rust will automatically 26 | determine the type from the context. 27 | 28 | You can construct a larger constant using the [bits] function. If you have a literal of up to 29 | 128 bits, it provides a functional form 30 | ``` 31 | # use rust_hdl::prelude::*; 32 | let x: Bits<200> = bits(0xDEAD_BEEE); // Works for up to 128 bit constants. 33 | ``` 34 | 35 | There is also the [ToBits] trait, which is implemented on the basic unsigned integer types. 36 | This trait allows you to handily convert from different integer values 37 | 38 | ``` 39 | # use rust_hdl::prelude::*; 40 | let x: Bits<10> = 32_u8.to_bits(); 41 | ``` 42 | -------------------------------------------------------------------------------- /rust-hdl/doc/md/bits_operations.md: -------------------------------------------------------------------------------- 1 | The [Bits](core::bits::Bits) type supports a subset of operations that can be synthesized in 2 | hardware. You can perform 3 | 4 | * Addition between `Bits` of the same size using the `+` operator 5 | * Subtraction between `Bits` of the same size using the `-` operator 6 | * Bitwise logical `AND` between `Bits` of the same size using the `&` operator 7 | * Bitwise logical `OR` between `Bits` of the same size using the `|` operator 8 | * Bitwise logical `XOR` (Exclusive Or) between `Bits` of the same size using the `^` operator 9 | * Bitwise comparisons for equality between `Bits` of the same size using `==` and `!=` operators 10 | * Unsigned comparisons (e.g., `>,>=,<,<=`) between `Bits` of the same size - these are 11 | always treated as unsigned values for comparison purposes. 12 | * Shift left using the `<<` operator 13 | * Shift right (no sign extension!) using the '>>' operator 14 | * Bitwise logical `NOT` using the `!` prefix operator 15 | 16 | These should feel natural when using RustHDL, as expressions follow Rust's rules (and not Verilog's). 17 | For example: 18 | ```rust 19 | # use rust_hdl::prelude::*; 20 | let x: Bits<32> = 0xDEAD_0000_u32.to_bits(); 21 | let y: Bits<32> = 0x0000_BEEF_u32.to_bits(); 22 | let z = x + y; 23 | assert_eq!(z, 0xDEAD_BEEF_u32.to_bits()); 24 | ``` 25 | 26 | You can, of course, construct expressions of arbitrary complexity using parenthesis, etc. 27 | The only real surprise may be at synthesis time, when you try to fit the expression onto hardware. 28 | 29 | -------------------------------------------------------------------------------- /rust-hdl/doc/md/features.md: -------------------------------------------------------------------------------- 1 | * Safe - have Rust check the validity of your firmware with 2 | strongly typed interfaces at **compile** time, as well as at 3 | run time, synthesis, and on the device. 4 | * Fast - Run simulations of your designs straight from your 5 | Rust code, with pretty good simulation performance. 6 | * Readable - RustHDL outputs Verilog code for synthesis and 7 | implementation, and goes through some effort to make sure that 8 | code is readable and understandable, in case you need to resolve 9 | timing issues or other conflicts. 10 | * Reusable - RustHDL supports templated firmware for parametric 11 | use, as well as a simple composition model based on structs. 12 | * Batteries Included - RustHDL includes a set of basic firmware 13 | widgets that provide FIFOs, RAMs and ROMs, Flip flops, SPI components, 14 | PWMs etc, so you can get started quickly. 15 | * Free - Although you can use RustHDL to wrap existing IP cores, 16 | all of the RustHDL code and firmware is open source and free to use 17 | (as in speech and beer). 18 | -------------------------------------------------------------------------------- /rust-hdl/src/docs/vcd2svg/interval.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone, PartialEq)] 2 | pub struct Interval { 3 | pub start_time: u64, 4 | pub end_time: u64, 5 | pub value: T, 6 | pub start_x: f64, 7 | pub end_x: f64, 8 | pub label: String, 9 | } 10 | 11 | impl Interval { 12 | pub fn is_empty(&self) -> bool { 13 | self.end_x == self.start_x 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /rust-hdl/src/docs/vcd2svg/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::docs::vcd2svg::display_metrics::DisplayMetrics; 2 | use crate::docs::vcd2svg::trace_collection::TraceCollection; 3 | use crate::docs::vcd2svg::vcd_style::VCDStyle; 4 | 5 | pub mod display_metrics; 6 | mod interval; 7 | mod renderable; 8 | pub mod symbols; 9 | pub mod text_frame; 10 | mod time_view; 11 | mod timed_value; 12 | pub mod trace_collection; 13 | mod utils; 14 | pub mod vcd_style; 15 | 16 | pub fn vcd_to_svg( 17 | vcd_filename: &str, 18 | svg_filename: &str, 19 | signal_names: &[&str], 20 | min_time_in_ps: u64, 21 | max_time_in_ps: u64, 22 | ) -> anyhow::Result<()> { 23 | let vcd = std::fs::File::open(vcd_filename)?; 24 | let traces = TraceCollection::parse(signal_names, vcd)?; 25 | let mut metrics = DisplayMetrics::default(); 26 | metrics.style = VCDStyle::gtkwave(); 27 | metrics.min_time = min_time_in_ps; 28 | metrics.max_time = max_time_in_ps; 29 | let document = traces.as_svg(&metrics)?; 30 | svg::save(svg_filename, &document)?; 31 | Ok(()) 32 | } 33 | 34 | pub fn vcd_to_txt( 35 | vcd_filename: &str, 36 | signal_names: &[&str], 37 | min_time_in_ps: u64, 38 | max_time_in_ps: u64, 39 | max_columns: u64, 40 | ) -> anyhow::Result { 41 | let vcd = std::fs::File::open(vcd_filename)?; 42 | let traces = TraceCollection::parse(signal_names, vcd)?; 43 | traces.as_string(min_time_in_ps, max_time_in_ps, max_columns as usize) 44 | } 45 | 46 | #[test] 47 | fn test_txt() { 48 | let msg = vcd_to_txt( 49 | &format!("{}/sims/ad868x.vcd", env!("CARGO_MANIFEST_DIR")), 50 | &[ 51 | "uut.clock", 52 | "uut.adc.spi_slave.state.q", 53 | "uut.master.clock", 54 | "uut.master.data_inbound", 55 | "uut.master.transfer_done", 56 | "uut.adc.spi_slave.bits_saved.d", 57 | ], 58 | 0, 59 | 250, 60 | 140, 61 | ) 62 | .unwrap(); 63 | println!("{}", msg); 64 | } 65 | -------------------------------------------------------------------------------- /rust-hdl/src/docs/vcd2svg/renderable.rs: -------------------------------------------------------------------------------- 1 | use num_bigint::BigInt; 2 | use std::clone::Clone; 3 | 4 | pub trait Renderable { 5 | fn render(&self) -> String; 6 | } 7 | 8 | impl Renderable for BigInt { 9 | fn render(&self) -> String { 10 | format!("0h{:x}", self) 11 | } 12 | } 13 | 14 | impl Renderable for String { 15 | fn render(&self) -> String { 16 | self.clone() 17 | } 18 | } 19 | 20 | impl Renderable for bool { 21 | fn render(&self) -> String { 22 | if *self { 23 | "1".to_string() 24 | } else { 25 | "0".to_string() 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /rust-hdl/src/docs/vcd2svg/symbols.rs: -------------------------------------------------------------------------------- 1 | // Adapted from tui-0.9.5/src/symbols 2 | pub const VERTICAL: char = '│'; 3 | pub const HORIZONTAL: char = '─'; 4 | pub const TOP_RIGHT: char = '┐'; 5 | pub const TOP_LEFT: char = '┌'; 6 | pub const BOTTOM_RIGHT: char = '┘'; 7 | pub const BOTTOM_LEFT: char = '└'; 8 | pub const HORIZONTAL_DOWN: char = '┬'; 9 | pub const HORIZONTAL_UP: char = '┴'; 10 | // Adapted from dwfv/src/tui/symbols 11 | pub const UP: char = '^'; 12 | pub const DOWN: char = 'v'; 13 | pub const DOUBLE_UP: char = '▲'; 14 | pub const DOUBLE_DOWN: char = '▼'; 15 | pub const LIGHT_UPPER: char = '╨'; 16 | pub const LIGHT_LOWER: char = '╥'; 17 | pub const LIGHT: char = '║'; 18 | pub const MEDIUM_UPPER: char = '┸'; 19 | pub const MEDIUM_LOWER: char = '┰'; 20 | pub const MEDIUM: char = '┃'; 21 | pub const FULL_UPPER: char = '▀'; 22 | pub const FULL_LOWER: char = '▄'; 23 | pub const FULL: char = '█'; 24 | pub const BLANK: char = ' '; 25 | -------------------------------------------------------------------------------- /rust-hdl/src/docs/vcd2svg/text_frame.rs: -------------------------------------------------------------------------------- 1 | // Inspired heavily by dwfv 2 | 3 | use crate::docs::vcd2svg::symbols; 4 | 5 | #[derive(Debug, Clone, Default)] 6 | pub struct TextFrame { 7 | buffers: Vec>, 8 | columns: usize, 9 | } 10 | 11 | impl TextFrame { 12 | pub fn new(columns: usize) -> Self { 13 | Self { 14 | buffers: vec![], 15 | columns, 16 | } 17 | } 18 | pub fn row(&mut self, row: usize) -> &mut [char] { 19 | if row < self.buffers.len() { 20 | &mut self.buffers[row] 21 | } else { 22 | while self.buffers.len() <= row { 23 | self.buffers.push(vec![' '; self.columns]) 24 | } 25 | &mut self.buffers[row] 26 | } 27 | } 28 | pub fn write(&mut self, row: usize, col: usize, msg: &str) { 29 | for (ndx, char) in msg.chars().enumerate() { 30 | if ndx + col < self.columns { 31 | self.row(row)[ndx + col] = char; 32 | } 33 | } 34 | } 35 | pub fn put(&mut self, row: usize, col: usize, x: char) { 36 | if col < self.columns { 37 | self.row(row)[col] = x; 38 | } 39 | } 40 | } 41 | 42 | impl ToString for TextFrame { 43 | fn to_string(&self) -> String { 44 | self.buffers 45 | .iter() 46 | .map(|x| x.iter().collect::()) 47 | .collect::>() 48 | .join("\n") 49 | } 50 | } 51 | 52 | #[test] 53 | fn test_frame_draw_stuff() { 54 | let mut x = TextFrame::new(16); 55 | x.write(0, 0, "hello world!"); 56 | assert_eq!(x.to_string(), "hello world! "); 57 | x.write(1, 0, "foo!"); 58 | assert_eq!(x.to_string(), "hello world! \nfoo! "); 59 | x.put(1, 1, symbols::HORIZONTAL); 60 | assert_eq!(x.to_string(), "hello world! \nf─o! "); 61 | } 62 | -------------------------------------------------------------------------------- /rust-hdl/src/docs/vcd2svg/time_view.rs: -------------------------------------------------------------------------------- 1 | use crate::docs::vcd2svg::interval::Interval; 2 | use crate::docs::vcd2svg::renderable::Renderable; 3 | use crate::docs::vcd2svg::timed_value::{SignalType, TimedValue}; 4 | use substring::Substring; 5 | 6 | #[derive(Clone, Debug)] 7 | pub struct TimeView { 8 | pub start_time: u64, 9 | pub end_time: u64, 10 | pub pixel_scale: f64, 11 | } 12 | 13 | impl TimeView { 14 | pub fn map(&self, time: u64) -> f64 { 15 | (self.start_time.max(time).min(self.end_time) - self.start_time) as f64 * self.pixel_scale 16 | } 17 | pub fn intervals(&self, vals: &[TimedValue]) -> Vec> { 18 | vals.windows(2) 19 | .map(|x| { 20 | let end_x = self.map(x[1].time); 21 | let start_x = self.map(x[0].time); 22 | let label_max = ((end_x - start_x) / 6.0).round() as usize; 23 | let mut label = x[0].value.render(); 24 | if label.len() > label_max { 25 | if label_max <= 3 { 26 | label = format!("!"); 27 | } else { 28 | label = format!("{}+", label.substring(0, label_max - 1)); 29 | } 30 | } 31 | Interval { 32 | start_time: x[0].time, 33 | end_time: x[1].time, 34 | value: x[0].value.clone(), 35 | start_x, 36 | end_x, 37 | label, 38 | } 39 | }) 40 | .collect() 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /rust-hdl/src/docs/vcd2svg/timed_value.rs: -------------------------------------------------------------------------------- 1 | use crate::docs::vcd2svg::renderable::Renderable; 2 | use num_bigint::BigInt; 3 | use std::fmt::Debug; 4 | 5 | pub trait SignalType: PartialEq + Clone + Debug + Default + Renderable {} 6 | 7 | impl SignalType for bool {} 8 | impl SignalType for BigInt {} 9 | impl SignalType for String {} 10 | 11 | #[derive(Clone, PartialEq, Debug)] 12 | pub struct TimedValue { 13 | pub time: u64, 14 | pub value: T, 15 | } 16 | 17 | pub fn changes(vals: &[TimedValue]) -> Vec> { 18 | if vals.is_empty() { 19 | vec![] 20 | } else { 21 | let mut prev = vals[0].clone(); 22 | let mut ret = vec![prev.clone()]; 23 | for val in vals { 24 | if val.value.ne(&prev.value) { 25 | ret.push(val.clone()); 26 | prev = val.clone(); 27 | } 28 | } 29 | ret 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /rust-hdl/src/docs/vcd2svg/utils.rs: -------------------------------------------------------------------------------- 1 | use num_bigint::{BigInt, Sign}; 2 | use svg::node::element::path::Data; 3 | use svg::node::element::Path; 4 | use vcd::Value; 5 | 6 | pub fn rect(x0: u32, y0: u32, x1: u32, y1: u32, color: &str) -> Path { 7 | let data = Data::new() 8 | .move_to((x0, y0)) 9 | .line_to((x1, y0)) 10 | .line_to((x1, y1)) 11 | .line_to((x0, y1)) 12 | .close(); 13 | let path = Path::new() 14 | .set("fill", color) 15 | .set("stroke", "none") 16 | .set("stroke-width", 0) 17 | .set("d", data); 18 | path 19 | } 20 | 21 | pub fn line(x0: u32, y0: u32, x1: u32, y1: u32, color: &str) -> Path { 22 | let data = Data::new().move_to((x0, y0)).line_to((x1, y1)); 23 | let path = Path::new() 24 | .set("fill", "none") 25 | .set("stroke", color) 26 | .set("stroke-width", 1) 27 | .set("d", data); 28 | path 29 | } 30 | 31 | pub fn value_to_bigint(v: &[Value]) -> Result { 32 | let bits = v 33 | .iter() 34 | .map(|x| match value_to_bool(x) { 35 | Ok(b) => { 36 | if b { 37 | Ok(1_u8) 38 | } else { 39 | Ok(0_u8) 40 | } 41 | } 42 | Err(e) => Err(e), 43 | }) 44 | .collect::, _>>()?; 45 | Ok(BigInt::from_radix_be(Sign::Plus, &bits, 2).unwrap()) 46 | } 47 | 48 | pub fn value_to_bool(v: &Value) -> Result { 49 | return match v { 50 | Value::V0 => Ok(false), 51 | Value::V1 => Ok(true), 52 | _ => Err(anyhow::Error::msg("Unsupported scalar signal type!")), 53 | }; 54 | } 55 | 56 | pub fn time_label(val: u64) -> String { 57 | if val < 1000 { 58 | format!("{}ps", val) 59 | } else if val < 1_000_000 { 60 | format!("{}ns", val / 1_000) 61 | } else if val < 1_000_000_000 { 62 | format!("{}us", val / 1_000_000) 63 | } else { 64 | format!("{}ms", val / 1_000_000_000) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /rust-hdl/src/docs/vcd2svg/vcd_style.rs: -------------------------------------------------------------------------------- 1 | pub struct VCDStyle { 2 | pub background_color: String, 3 | pub trace_color: String, 4 | pub timeline_background_color: String, 5 | pub timeline_line_color: String, 6 | pub timeline_tick_color: String, 7 | pub signal_label_background_color: String, 8 | pub grid_lines: Option, 9 | } 10 | 11 | impl VCDStyle { 12 | pub fn scansion() -> VCDStyle { 13 | Self { 14 | background_color: "#282828".into(), 15 | signal_label_background_color: "#e8e8e8".into(), 16 | timeline_background_color: "#f3f5de".into(), 17 | timeline_line_color: "#cbcbcb".into(), 18 | timeline_tick_color: "#000000".into(), 19 | trace_color: "#87ecd1".into(), 20 | grid_lines: None, 21 | } 22 | } 23 | pub fn gtkwave() -> VCDStyle { 24 | Self { 25 | background_color: "#000000".into(), 26 | signal_label_background_color: "#e8e8e8".into(), 27 | timeline_background_color: "#000000".into(), 28 | timeline_line_color: "#cbcbcb".into(), 29 | timeline_tick_color: "#FFFFFF".into(), 30 | trace_color: "#00ff00".into(), 31 | grid_lines: Some("#202070".into()), 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /rust-hdl/src/fig.md: -------------------------------------------------------------------------------- 1 | 2 | *bar* 3 | 4 | 5 | 21 | 22 | Here is some more random markdown 23 | 24 | - Stuff is cool 25 | - and junk 26 | -------------------------------------------------------------------------------- /rust-hdl/src/prelude.rs: -------------------------------------------------------------------------------- 1 | pub use crate::docs::vcd2svg::vcd_to_svg; 2 | pub use rust_hdl_core::prelude::*; 3 | pub use rust_hdl_hls::prelude::*; 4 | pub use rust_hdl_sim::prelude::*; 5 | pub use rust_hdl_widgets::prelude::*; 6 | -------------------------------------------------------------------------------- /rust-hdl/tests/core_dff_with_init.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl::prelude::*; 2 | 3 | #[derive(LogicBlock)] 4 | struct DFFWithNonzeroInit { 5 | clock: Signal, 6 | dff: DFFWithInit>, 7 | pub count: Signal>, 8 | } 9 | 10 | impl Default for DFFWithNonzeroInit { 11 | fn default() -> Self { 12 | Self { 13 | clock: Default::default(), 14 | dff: DFFWithInit::new(42.into()), 15 | count: Default::default(), 16 | } 17 | } 18 | } 19 | 20 | impl Logic for DFFWithNonzeroInit { 21 | #[hdl_gen] 22 | fn update(&mut self) { 23 | dff_setup!(self, clock, dff); 24 | self.count.next = self.dff.q.val(); 25 | self.dff.d.next = self.dff.q.val() + 1; 26 | } 27 | } 28 | 29 | #[test] 30 | fn test_dff_with_nonzero_init() { 31 | let mut uut = DFFWithNonzeroInit::default(); 32 | uut.connect_all(); 33 | let mut sim = Simulation::new(); 34 | sim.add_clock(5, |x: &mut Box| { 35 | x.clock.next = !x.clock.val() 36 | }); 37 | sim.add_testbench(move |mut sim: Sim| { 38 | let mut x = sim.init()?; 39 | sim_assert_eq!(sim, x.count.val(), 42, x); 40 | for i in 0..12 { 41 | wait_clock_cycle!(sim, clock, x); 42 | sim_assert_eq!(sim, x.count.val(), 42 + i + 1, x); 43 | } 44 | sim.done(x) 45 | }); 46 | sim.run_to_file(Box::new(uut), 10000, &vcd_path!("dff_non_zero.vcd")) 47 | .unwrap() 48 | } 49 | -------------------------------------------------------------------------------- /rust-hdl/tests/core_fir_test.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl::prelude::*; 2 | 3 | #[test] 4 | fn test_fir_is_synthesizable() { 5 | let coeffs = [1_i16, 2, 3, 2, 1]; 6 | let mut uut = MultiplyAccumulateSymmetricFiniteImpulseResponseFilter::<3>::new(&coeffs); 7 | uut.connect_all(); 8 | let vlog = generate_verilog(&uut); 9 | yosys_validate("fir_synth", &vlog).unwrap(); 10 | } 11 | 12 | #[test] 13 | fn test_fir_impulse_response_is_expected() { 14 | type MACFIRTest = MultiplyAccumulateSymmetricFiniteImpulseResponseFilter<3>; 15 | let coeffs = [1_i16, 3, 4, 5, 4, 3, 1]; 16 | let mut uut = MACFIRTest::new(&coeffs); 17 | uut.connect_all(); 18 | let vlog = generate_verilog(&uut); 19 | yosys_validate("fir_sim", &vlog).unwrap(); 20 | let mut sim = Simulation::new(); 21 | sim.add_clock(5, |x: &mut Box| x.clock.next = !x.clock.val()); 22 | sim.add_testbench(move |mut sim: Sim| { 23 | let mut x = sim.init()?; 24 | wait_clock_true!(sim, clock, x); 25 | for i in 0..15 { 26 | x = sim.watch(|x| x.strobe_out.val(), x)?; 27 | println!("Output value: {} -> {:x}", i, x.data_out.val()); 28 | x = sim.watch(|x| !x.strobe_out.val(), x)?; 29 | } 30 | sim.done(x) 31 | }); 32 | sim.add_testbench(move |mut sim: Sim| { 33 | let mut x = sim.init()?; 34 | wait_clock_true!(sim, clock, x); 35 | for val in [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0] { 36 | x.data_in.next = val.into(); 37 | x.strobe_in.next = true; 38 | wait_clock_cycle!(sim, clock, x); 39 | x.strobe_in.next = false; 40 | wait_clock_cycles!(sim, clock, x, 15); 41 | } 42 | sim.done(x)?; 43 | Ok(()) 44 | }); 45 | let mut x = MACFIRTest::new(&coeffs); 46 | x.connect_all(); 47 | sim.run_traced( 48 | Box::new(x), 49 | 10000, 50 | std::fs::File::create(vcd_path!("fir.vcd")).unwrap(), 51 | ) 52 | .unwrap() 53 | } 54 | -------------------------------------------------------------------------------- /rust-hdl/tests/core_hls_sdram_fifo.rs: -------------------------------------------------------------------------------- 1 | use rand::Rng; 2 | use rust_hdl::prelude::*; 3 | 4 | #[derive(LogicBlock)] 5 | struct HLSSDRAMFIFOTest { 6 | fifo: SDRAMFIFO<5, 5, 4, 16, 12>, 7 | sdram: SDRAMSimulator<5, 5, 10, 16>, 8 | clock: Signal, 9 | } 10 | 11 | impl Default for HLSSDRAMFIFOTest { 12 | fn default() -> Self { 13 | let timings = MemoryTimings::fast_boot_sim(125e6); 14 | Self { 15 | fifo: SDRAMFIFO::new(3, timings, OutputBuffer::Wired), 16 | sdram: SDRAMSimulator::new(timings), 17 | clock: Default::default(), 18 | } 19 | } 20 | } 21 | 22 | impl Logic for HLSSDRAMFIFOTest { 23 | #[hdl_gen] 24 | fn update(&mut self) { 25 | clock!(self, clock, fifo); 26 | self.fifo.ram_clock.next = self.clock.val(); 27 | SDRAMDriver::<16>::join(&mut self.fifo.sdram, &mut self.sdram.sdram); 28 | } 29 | } 30 | 31 | #[test] 32 | fn test_hls_sdram_fifo_synthesizes() { 33 | let mut uut = HLSSDRAMFIFOTest::default(); 34 | uut.fifo.bus_write.link_connect_dest(); 35 | uut.fifo.bus_read.link_connect_dest(); 36 | uut.connect_all(); 37 | let vlog = generate_verilog(&uut); 38 | yosys_validate("hls_sdram_fifo", &vlog).unwrap(); 39 | } 40 | 41 | #[test] 42 | fn test_hls_sdram_fifo_works() { 43 | let mut uut = HLSSDRAMFIFOTest::default(); 44 | uut.fifo.bus_write.link_connect_dest(); 45 | uut.fifo.bus_read.link_connect_dest(); 46 | uut.connect_all(); 47 | let mut sim = Simulation::new(); 48 | let data = (0..256) 49 | .map(|_| rand::thread_rng().gen::()) 50 | .collect::>(); 51 | let data2 = data.clone(); 52 | sim.add_clock(4000, |x: &mut Box| { 53 | x.clock.next = !x.clock.val() 54 | }); 55 | sim.add_testbench(move |mut sim: Sim| { 56 | let mut x = sim.init()?; 57 | wait_clock_cycles!(sim, clock, x, 20); 58 | hls_fifo_write_lazy!(sim, clock, x, fifo.bus_write, &data); 59 | sim.done(x) 60 | }); 61 | sim.add_testbench(move |mut sim: Sim| { 62 | let mut x = sim.init()?; 63 | wait_clock_cycles!(sim, clock, x, 20); 64 | hls_fifo_read_lazy!(sim, clock, x, fifo.bus_read, &data2); 65 | sim.done(x) 66 | }); 67 | sim.run_to_file(Box::new(uut), 200_000_000, &vcd_path!("hls_sdram_fifo.vcd")) 68 | .unwrap(); 69 | } 70 | -------------------------------------------------------------------------------- /rust-hdl/tests/core_lfsr_widget.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl::prelude::*; 2 | 3 | struct Xorshift128State { 4 | x: [u32; 4], 5 | } 6 | 7 | const SEED: u128 = 0x843233523a613966423b622562592c62; 8 | 9 | impl Default for Xorshift128State { 10 | fn default() -> Self { 11 | Self { 12 | x: [ 13 | ((SEED >> 96) & 0xFFFF_FFFF_u128) as u32, 14 | ((SEED >> 64) & 0xFFFF_FFFF_u128) as u32, 15 | ((SEED >> 32) & 0xFFFF_FFFF_u128) as u32, 16 | ((SEED >> 0) & 0xFFFF_FFFF_u128) as u32, 17 | ], 18 | } 19 | } 20 | } 21 | 22 | impl Xorshift128State { 23 | fn get(&mut self) -> u32 { 24 | let ret = self.x[0]; 25 | let mut t = self.x[3]; 26 | let s = self.x[0]; 27 | self.x[3] = self.x[2]; 28 | self.x[2] = self.x[1]; 29 | self.x[1] = s; 30 | t ^= t << 11; 31 | t ^= t >> 8; 32 | self.x[0] = t ^ s ^ (s >> 19); 33 | ret 34 | } 35 | } 36 | 37 | #[test] 38 | fn test_lfsr_operation() { 39 | let mut uut = LFSRSimple::default(); 40 | uut.connect_all(); 41 | let mut sim = Simulation::new(); 42 | sim.add_clock(5, |x: &mut Box| { 43 | x.clock.next = !x.clock.val(); 44 | }); 45 | sim.add_testbench(move |mut sim: Sim| { 46 | let mut x = sim.init()?; 47 | let mut lf = Xorshift128State::default(); 48 | wait_clock_cycles!(sim, clock, x, 10); 49 | for _ in 0..1000 { 50 | sim_assert_eq!(sim, x.num.val().index() as u32, lf.get(), x); 51 | x.strobe.next = true; 52 | wait_clock_cycle!(sim, clock, x); 53 | } 54 | sim.done(x) 55 | }); 56 | sim.run_to_file(Box::new(uut), 100_000, &vcd_path!("lfsr.vcd")) 57 | .unwrap(); 58 | } 59 | -------------------------------------------------------------------------------- /rust-hdl/tests/core_pwm.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl::prelude::*; 2 | use std::fs::File; 3 | 4 | #[derive(LogicBlock)] 5 | struct PWMTest { 6 | pub clock: Signal, 7 | pub pwm: PulseWidthModulator<8>, 8 | } 9 | 10 | impl Default for PWMTest { 11 | fn default() -> Self { 12 | Self { 13 | clock: Signal::default(), 14 | pwm: PulseWidthModulator::default(), 15 | } 16 | } 17 | } 18 | 19 | impl Logic for PWMTest { 20 | #[hdl_gen] 21 | fn update(&mut self) { 22 | clock!(self, clock, pwm); 23 | self.pwm.enable.next = true; 24 | self.pwm.threshold.next = 32.into(); 25 | } 26 | } 27 | 28 | #[test] 29 | fn test_pwm_circuit() { 30 | let mut uut = PWMTest::default(); 31 | uut.connect_all(); 32 | yosys_validate("pwm", &generate_verilog(&uut)).unwrap(); 33 | let mut sim = Simulation::new(); 34 | sim.add_clock(5, |x: &mut Box| x.clock.next = !x.clock.val()); 35 | sim.add_testbench(|mut sim: Sim| { 36 | let mut x = sim.init()?; 37 | let mut accum = 0; 38 | for _ndx in 0..256 { 39 | x = sim.wait(10, x)?; 40 | if x.pwm.active.val() { 41 | accum += 1; 42 | } 43 | } 44 | sim.done(x)?; 45 | assert_eq!(accum, 32); 46 | Ok(()) 47 | }); 48 | sim.run_traced( 49 | Box::new(uut), 50 | 512 * 10, 51 | File::create(vcd_path!("pwm.vcd")).unwrap(), 52 | ) 53 | .unwrap(); 54 | } 55 | -------------------------------------------------------------------------------- /rust-hdl/tests/core_rom.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl::prelude::*; 2 | use std::collections::BTreeMap; 3 | use std::fs::File; 4 | 5 | #[derive(LogicBlock)] 6 | struct ROMTest { 7 | rom: ROM, 4>, 8 | } 9 | 10 | impl ROMTest { 11 | pub fn new() -> ROMTest { 12 | let mut rom = BTreeMap::new(); 13 | for i in 0..16 { 14 | rom.insert(Bits::<4>::from(i), Bits::<4>::from(15 - i)); 15 | } 16 | ROMTest { rom: ROM::new(rom) } 17 | } 18 | } 19 | 20 | impl Logic for ROMTest { 21 | fn update(&mut self) {} 22 | } 23 | 24 | #[test] 25 | fn test_synthesis_rom() { 26 | let mut uut = ROMTest::new(); 27 | uut.rom.address.connect(); 28 | uut.connect_all(); 29 | let vlog = generate_verilog(&uut); 30 | yosys_validate("rom", &vlog).unwrap(); 31 | } 32 | 33 | #[test] 34 | fn test_rom_works() { 35 | let mut sim = Simulation::new(); 36 | sim.add_testbench(|mut sim: Sim| { 37 | let mut x = sim.init()?; 38 | for i in 0..16 { 39 | x.rom.address.next = Bits::<4>::from(i).into(); 40 | x = sim.wait(1, x)?; 41 | assert_eq!(x.rom.data.val(), Bits::<4>::from(15 - i)); 42 | } 43 | sim.done(x)?; 44 | Ok(()) 45 | }); 46 | let mut dut = ROMTest::new(); 47 | dut.rom.address.connect(); 48 | dut.connect_all(); 49 | sim.run_traced( 50 | Box::new(dut), 51 | 100, 52 | File::create(vcd_path!("ROM.vcd")).unwrap(), 53 | ) 54 | .unwrap(); 55 | } 56 | -------------------------------------------------------------------------------- /rust-hdl/tests/core_signed.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl::prelude::*; 2 | 3 | #[derive(LogicBlock)] 4 | struct CircuitSigned { 5 | x: Signal>, 6 | y: Constant>, 7 | z: Signal>, 8 | } 9 | 10 | impl Logic for CircuitSigned { 11 | #[hdl_gen] 12 | fn update(&mut self) { 13 | self.z.next = self.x.val() + self.y.val(); 14 | } 15 | } 16 | 17 | impl Default for CircuitSigned { 18 | fn default() -> Self { 19 | Self { 20 | x: Default::default(), 21 | y: Constant::new(Signed::from(-4935)), 22 | z: Default::default(), 23 | } 24 | } 25 | } 26 | 27 | #[test] 28 | fn signed_vals_synthesize() { 29 | let mut uut = CircuitSigned::default(); 30 | uut.connect_all(); 31 | let vlog = generate_verilog(&uut); 32 | yosys_validate("signed", &vlog).unwrap() 33 | } 34 | 35 | // A test comparitor that makes a signed comparison internally 36 | #[derive(LogicBlock, Default)] 37 | struct TestCircuit { 38 | x: Signal>, 39 | y: Signal>, 40 | z: Signal, 41 | } 42 | 43 | impl Logic for TestCircuit { 44 | #[hdl_gen] 45 | fn update(&mut self) { 46 | self.z.next = signed_cast(self.x.val()) < signed_cast(self.y.val()); 47 | } 48 | } 49 | 50 | #[test] 51 | fn to_signed_bits_inline_works_issue_2() { 52 | use rand::Rng; 53 | let mut uut = TestCircuit::default(); 54 | uut.connect_all(); 55 | let vlog = generate_verilog(&uut); 56 | assert!(vlog.contains("$signed(x)")); 57 | assert!(vlog.contains("$signed(y)")); 58 | yosys_validate("to_signed_bits", &vlog).unwrap(); 59 | (0..100_000).for_each(|_| { 60 | let (x, y) = rand::thread_rng().gen::<(u32, u32)>(); 61 | uut.x.next = (x as u64).into(); 62 | uut.y.next = (y as u64).into(); 63 | assert!(simulate(&mut uut, 10)); 64 | let cmp = (x as i32) < (y as i32); 65 | assert_eq!(uut.z.val(), cmp); 66 | }); 67 | } 68 | -------------------------------------------------------------------------------- /rust-hdl/tests/core_soc.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl::prelude::*; 2 | 3 | #[test] 4 | fn test_soc_test_chip_synthesizes() { 5 | let mut uut = SoCTestChip::default(); 6 | uut.connect_all(); 7 | let vlog = generate_verilog(&uut); 8 | yosys_validate("soc_test", &vlog).unwrap(); 9 | } 10 | -------------------------------------------------------------------------------- /rust-hdl/tests/core_spi_mux.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl::prelude::*; 2 | 3 | #[derive(LogicBlock)] 4 | struct SPITestMultiMaster { 5 | clock: Signal, 6 | masters: [SPIMaster<64>; 3], 7 | addr: Signal>, 8 | mux: MuxMasters<3, 3>, 9 | slave: SPISlave<64>, 10 | } 11 | 12 | impl SPITestMultiMaster { 13 | pub fn new(config: SPIConfig) -> Self { 14 | Self { 15 | clock: Default::default(), 16 | masters: array_init::array_init(|_| SPIMaster::new(config)), 17 | addr: Default::default(), 18 | mux: Default::default(), 19 | slave: SPISlave::new(config), 20 | } 21 | } 22 | } 23 | 24 | impl Logic for SPITestMultiMaster { 25 | #[hdl_gen] 26 | fn update(&mut self) { 27 | for i in 0..3 { 28 | self.masters[i].clock.next = self.clock.val(); 29 | SPIWiresMaster::join(&mut self.masters[i].wires, &mut self.mux.from_masters[i]); 30 | } 31 | SPIWiresMaster::join(&mut self.mux.to_bus, &mut self.slave.wires); 32 | clock!(self, clock, slave); 33 | self.mux.sel.next = self.addr.val(); 34 | } 35 | } 36 | 37 | #[test] 38 | fn test_spi_mux() { 39 | let config = SPIConfig { 40 | clock_speed: 48_000_000, 41 | cs_off: true, 42 | mosi_off: true, 43 | speed_hz: 1_000_000, 44 | cpha: true, 45 | cpol: true, 46 | }; 47 | let mut uut = SPITestMultiMaster::new(config); 48 | for i in 0..3 { 49 | uut.masters[i].continued_transaction.connect(); 50 | uut.masters[i].start_send.connect(); 51 | uut.masters[i].data_outbound.connect(); 52 | uut.masters[i].bits_outbound.connect(); 53 | } 54 | uut.slave.data_outbound.connect(); 55 | uut.slave.start_send.connect(); 56 | uut.slave.continued_transaction.connect(); 57 | uut.slave.disabled.connect(); 58 | uut.slave.bits.connect(); 59 | uut.connect_all(); 60 | yosys_validate("spi_mux_multi_master", &generate_verilog(&uut)).unwrap(); 61 | } 62 | -------------------------------------------------------------------------------- /rust-hdl/tests/core_sync_reset.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl::prelude::*; 2 | 3 | #[test] 4 | fn test_sync_reset() { 5 | let mut uut = AutoReset::default(); 6 | uut.connect_all(); 7 | let mut sim = Simulation::new(); 8 | sim.add_clock(5000, |x: &mut Box| x.clock.next = !x.clock.val()); 9 | sim.add_testbench(move |mut sim: Sim| { 10 | let mut x = sim.init()?; 11 | x = sim.wait(15_000, x)?; 12 | sim.done(x) 13 | }); 14 | sim.run_to_file(Box::new(uut), 20_000, &vcd_path!("sr_test.vcd")) 15 | .unwrap(); 16 | } 17 | -------------------------------------------------------------------------------- /rust-hdl/tests/core_sync_rom.rs: -------------------------------------------------------------------------------- 1 | use rust_hdl::prelude::*; 2 | use std::collections::BTreeMap; 3 | 4 | #[derive(LogicBlock)] 5 | struct SyncROMTest { 6 | rom: SyncROM, 4>, 7 | } 8 | 9 | impl SyncROMTest { 10 | pub fn new() -> SyncROMTest { 11 | let mut rom = BTreeMap::new(); 12 | for i in 0..16 { 13 | rom.insert(Bits::<4>::from(i), Bits::<4>::from(15 - i)); 14 | } 15 | SyncROMTest { 16 | rom: SyncROM::new(rom), 17 | } 18 | } 19 | } 20 | 21 | impl Logic for SyncROMTest { 22 | fn update(&mut self) {} 23 | } 24 | 25 | #[test] 26 | fn test_synthesis_sync_rom() { 27 | let mut uut = SyncROMTest::new(); 28 | uut.rom.address.connect(); 29 | uut.rom.clock.connect(); 30 | uut.connect_all(); 31 | let vlog = generate_verilog(&uut); 32 | yosys_validate("srom", &vlog).unwrap(); 33 | } 34 | --------------------------------------------------------------------------------