Remember the post I wrote about 2 months ago ? Asking why you don't write much hardware?
In it I said I was writing a library to make it easier. Well, I have completed the initial release version of it π π π
Quick note : I don't usually ask this, but please share this article, or directly the library on your socials or such! I have spent quite some time on developing it, and I really want people who can benefit from this to use this. Your share might help this reach someone who needs it. Thank you!
So, what is it?
Well, I'm happy to share with you : pcb-rs
! This is a library which will make it much easier for you to write hardware components, and simulate them. It does this by giving you two proc-macros, which you can use with your run-of-the-mill struct
s and it will generate the interface implementation for you!
Why ???
Because I want to make it easier for people who wish to explore hardware to do so. This does not aim to be the perfect and most-accurate-simulation ever ; or try to replace VHDL, (because it does not). This aims to be a rung in the ladder from high-level programming language to hardware level. That way those who are just starting to explore it, will have to jump a little less height : they can still work in comfort of Rust, but can have the taste of things that one has to consider when designing hardware.
It is choose-your-own-difficulty hardware design!
Show, don't tell
Here I'm showing how you can write a simple 256-byte RAM as a hardware component using my library :
use pcb_rs::Chip;
#[derive(Chip)]
pub struct Memory {
#[pin(input)]
addr: u8,
#[pin(io, io_latch)]
data: Option<u8>,
#[pin(input)]
is_active: bool,
#[pin(input)]
is_read: bool,
io_latch: bool,
pub mem: [u8; 256],
}
impl Chip for Memory {
fn tick(&mut self) {
if !self.is_active {
self.io_latch = true;
return;
}
if self.is_read {
let addr = self.addr as usize;
let data = self.mem[addr];
self.data = Some(data);
self.io_latch = false;
} else {
let addr = self.addr as usize;
let val = self.data.unwrap();
self.mem[addr] = val;
}
}
}
Here the derive(Chip)
is my prco-macro which automatically implements the required traits for your chip, and you just have to write impl Chip
which defines the processing logic of your chip.
Thus in about ~33 lines, we have a working RAM. This is also modular - it can be used with other components, such as CPU π π And pcb-rs also provides another macro for that :
pcb!(PCB{
chip cpu;
chip mem;
cpu::addr_bus - mem::addr;
cpu::data_bus - mem::data;
cpu::mem_active - mem::is_active;
cpu::read_mem - mem::is_read;
});
Yep. That's it. That's all you have to define to interface a CPU and the RAM. the pcb!
macro will parse this, derive the logic for validating the chips given to it , derive the logic to verify the pin connection, derive the logic for passing values from one chip to another, and write the builer and pcb struct.
Now to actually place the RAM and CPU pins in it:
let mem = Box::new(Memory::new());
let cpu = Box::new(CPU::new());
let temp::PCBBuilder = PCBBuilder::new();
let temp = temp.add_chip("cpu", cpu).add_chip("mem", mem);
let mut pcb:PCB = temp.build().unwrap();
pcb.tick();
This will place the chips in the builder, validate that correct chips have been placed, and give you the PCB struct, which contains all the running logic after calling build. The PCB itself will have the tick()
method, which will run your chips.
And you know what, this itself is modular! You can use such chips as part of some other PCB, and so on π π
Wait, you're telling me this is easier?
Well, yeah! This will allow you to write Hardware components from comfort of Rust, before you get into something more complex as VHDL. Also you have to write just the struct and annotate what members you want to use as pins, and then write the processing logic for the chip. Everything else is derived by the macro for you.
Same for pcb, you just have to define the chips, connections and what pins you want to expose to others, rest, including the actual struct, will be derived by the macro.
Ok, show me more
I know this is pretty different that what many of you might be usually doing. So, as examples, I have written several components that range from pretty basic to a bit complex.
You know what's more? As Rust can compile to WASM, I have also done that and have made a web interface for the examples. You can run them directly from your browsers. Check it out at https://yjdoc2.github.io/pcb-rs-examples/.
Currently the examples consist of :
- Basic gates such as Not, And, Or, Xor wikipedia
- Ripple carry adder wikipedia
- Look ahead Carry adder wikipedia
- SR, D, T, JK Latches wikipedia
- Straight and Twisted Ring counters wikipedia
- CPU and PCB example which I showed above
You can try all these right in your browser, at https://yjdoc2.github.io/pcb-rs-examples/.
I made these particular examples, as these are usually a part of basic hardware/electronics course, so these will be a good taste of how pcb-rs library can be used for that.
Links, Links and Links
So,
The library :
Pcb-rs
A library to easily wite Software Emulated Hardware
This library provides two macros Chip
and pcb
which can be used to write software emulated hardware components. Chip
is a derive macro which can be used on structs to automatically implement the necessary interfaces for the struct to be treated as a Hardware Chip, and you only need to implement the tick function which will be called on each clock cycle to run the logic of your chip. pcb
macro is used to define a PCB , where you can connect multiple chips, and it will manage connecting pins of chips, verifying the connections and passing the data on the connected chip.
One of the aims of this library is modularity and reusability, thus the pcb created can be further used as chips in some other pcb ans so on.
There are some finer points to be noted when creatingβ¦
The examples :
YJDoc2 / pcb-rs-examples
Repository containing examples of usage of pcb-rs
PCB-RS Examples
This repository contains examples to demonstrate use of pcb-rs. To see more information about what is pcb-rs, the reasoning behind it, and basic syntax and examples, visit its repository at https://www.github.com/YJDoc2/pcb-rs.
This repository contains various examples on how to write components, as well as how to reuse components from other libraries to create a composite component. Each of the directories contain their own readme describing the that particular example.
The web version of these examples is hosted at yjdoc2.github.io/pcb-rs-examples/, so you can try them right in your browser without needing to install anything, as well as show the possibilities with this library and WASM for making such electronics related tools and simulations.
Various directories in this repo contain different examples, and their details are explained in their own readmes in that directory. Currently this repo contains following directories :
- Basic Gates : This contains basicβ¦
The Web interface for the examples : https://yjdoc2.github.io/pcb-rs-examples/.
And ...?
Go Check It Out!!! Try it out in your own projects, share the projects and let me know how are you using it!
And as I said in the start, I don't usually ask this, but please consider sharing this. I really want this to help people who want to get into hardware. Your share will help this reach more people.
Also if you think this is interesting, consider starring it on GitHub. That might also help getting this more attention.
Let me know in comments your thoughts and reviews for this.
Thanks a lot for reading!
Note : header image is taken from google images search, and is not mine.
Top comments (1)
PCB-rs is revolutionizing hardware development by simplifying PCB design. This innovative tool streamlines the process, making it accessible even for newcomers. With features like UETPCB integration, it ensures seamless design from concept to production, empowering engineers to focus on creativity rather than technical hurdles. PCB-rs heralds a new era of efficiency in hardware innovation.