app: multi_core_inverter: try a 5-stage inverter (each stage inverts)
we're diverging from the blog pretty far now. but it turns out that, because of the inversion in Maxwell's $\nabla x E = -dB/dT$ equation, the trivial wiring actually leads to natural inverters.
This commit is contained in:
@@ -94,23 +94,30 @@ fn main() {
|
||||
let input_magnitude = 1.0e9;
|
||||
let clock_phase_duration = ps(2000);
|
||||
|
||||
// 's' = core (ferromagnetic part)
|
||||
let s_major = um(160);
|
||||
let s_minor = um(30);
|
||||
// 'io' = drive/control wire
|
||||
let io_major = um(80);
|
||||
let io_minor = um(30);
|
||||
let coupling_major = um(130);
|
||||
let coupling_minor = um(30);
|
||||
|
||||
// coords for core 'n'
|
||||
let sx = |n| um((n+1) * 400);
|
||||
let sy = um(400);
|
||||
let sz = um(280);
|
||||
// x-coord for the loop coupling core 'n' to core 'n+1'
|
||||
let couplingx = |n| sx(n) + um(200);
|
||||
|
||||
let sim_bounds = Meters::new(sx(4), sy * 2.0, sz * 2.0);
|
||||
let sim_bounds = |num_cores| Meters::new(sx(num_cores), sy * 2.0, sz * 2.0);
|
||||
let sim_padding = Meters::new(um(80), um(80), um(80));
|
||||
|
||||
let drive0 = Torus::new_xz(Meters::new(sx(0) - s_major, sy, sz), io_major, io_minor);
|
||||
let sense3 = Torus::new_xz(Meters::new(sx(3) + s_major, sy, sz), io_major, io_minor);
|
||||
// core '0' gets an external input in place of its coupling loop
|
||||
let input0 = Torus::new_xz(Meters::new(sx(0) - s_major, sy, sz), io_major, io_minor);
|
||||
// the last core gets an external output in place of its coupling loop
|
||||
let sense = |n| Torus::new_xz(Meters::new(sx(n) + s_major, sy, sz), io_major, io_minor);
|
||||
// control loop for core n (alternately called "drive" loop)
|
||||
let ctl = |n| Torus::new_yz(Meters::new(sx(n), sy + s_major, sz), io_major, io_minor);
|
||||
let s = |n| Torus::new_xy(Meters::new(sx(n), sy, sz), s_major, s_minor);
|
||||
// coupling(n) is the wire which couples core n into core n+1
|
||||
@@ -118,8 +125,7 @@ fn main() {
|
||||
|
||||
let control_signal = |driver: &mut Driver<Sim>, region: &Torus, cycle: u32, ty: DriveType| {
|
||||
let area = region.cross_section();
|
||||
// negation because blog confuses current directions.
|
||||
let amp = -ty.direction() as f32 * input_magnitude / area;
|
||||
let amp = ty.direction() as f32 * input_magnitude / area;
|
||||
let start = clock_phase_duration * cycle as f32;
|
||||
if ty.is_hold() {
|
||||
// simple square wave:
|
||||
@@ -144,114 +150,98 @@ fn main() {
|
||||
// .shifted(clock_phase_duration * cycle as f32);
|
||||
};
|
||||
|
||||
//////// define the control signals/transitions
|
||||
// each row N denotes the drive currents at clock cycle N.
|
||||
// each col M denotes the drive current at core M.
|
||||
use DriveType::*;
|
||||
let drive_map = [
|
||||
// charge each device to '1'
|
||||
[HoldHigh, HoldHigh, HoldHigh, HoldHigh, HoldHigh],
|
||||
// open S1 for write
|
||||
[HoldHigh, ReleaseHigh, HoldHigh, HoldHigh, HoldHigh],
|
||||
|
||||
// write S0' => S1
|
||||
[HoldLow, Float, HoldHigh, HoldHigh, HoldHigh],
|
||||
// open S2 for write
|
||||
[HoldLow, Float, ReleaseHigh, HoldHigh, HoldHigh],
|
||||
|
||||
// write S1' => S2
|
||||
[HoldLow, HoldLow, Float, HoldHigh, HoldLow],
|
||||
// open S3 for write
|
||||
[HoldLow, HoldLow, Float, ReleaseHigh, HoldLow],
|
||||
|
||||
// write S2' => S3; RESET S0
|
||||
[HoldHigh, HoldLow, HoldLow, Float, HoldLow],
|
||||
// open S4 for write
|
||||
[HoldHigh, HoldLow, HoldLow, Float, ReleaseLow],
|
||||
|
||||
// write S3' => S4; RESET S1
|
||||
[HoldHigh, HoldHigh, HoldLow, HoldLow, Float],
|
||||
// open S0 for write
|
||||
[ReleaseHigh, HoldHigh, HoldLow, HoldLow, Float],
|
||||
|
||||
// write S4' => output; RESET S2
|
||||
[Float, HoldHigh, HoldHigh, HoldLow, HoldLow],
|
||||
// open S1 for write
|
||||
[Float, ReleaseHigh, HoldHigh, HoldLow, HoldLow],
|
||||
];
|
||||
|
||||
let wire_mat = IsomorphicConductor::new(1e6f32.cast::<R>());
|
||||
// let ferro_mat = wire_mat;
|
||||
let ferro_mat = Ferroxcube3R1MH::new();
|
||||
|
||||
let num_cores = drive_map[0].len();
|
||||
let last_core = num_cores - 1;
|
||||
|
||||
let mut driver = Driver::new(Sim::new(
|
||||
sim_bounds.to_index(feat_size), feat_size,
|
||||
sim_bounds(num_cores).to_index(feat_size), feat_size,
|
||||
));
|
||||
driver.add_classical_boundary_explicit::<R, _>(sim_padding);
|
||||
|
||||
//////// create the wires and toroids
|
||||
driver.fill_region(&drive0, wire_mat);
|
||||
driver.fill_region(&sense3, wire_mat);
|
||||
for core in 0..4 {
|
||||
driver.fill_region(&input0, wire_mat);
|
||||
driver.fill_region(&sense(last_core), wire_mat);
|
||||
for core in 0..num_cores {
|
||||
driver.fill_region(&s(core), ferro_mat);
|
||||
driver.fill_region(&ctl(core), wire_mat);
|
||||
if core != 3 {
|
||||
if core != last_core {
|
||||
driver.fill_region(&coupling(core), wire_mat);
|
||||
}
|
||||
}
|
||||
|
||||
//////// monitor some measurements
|
||||
// driver.add_measurement(meas::CurrentLoop::new("drv0", drive0.clone()));
|
||||
for core in 0..4 {
|
||||
// driver.add_measurement(meas::CurrentLoop::new("input0", input0.clone()));
|
||||
for core in 0..num_cores {
|
||||
driver.add_measurement(meas::CurrentLoop::new(
|
||||
&format!("drive{}", core),
|
||||
ctl(core),
|
||||
));
|
||||
}
|
||||
for core in 0..3 {
|
||||
driver.add_measurement(meas::CurrentLoop::new(
|
||||
&format!("sense{}", core),
|
||||
coupling(core),
|
||||
));
|
||||
for core in 0..num_cores {
|
||||
let name = format!("sense{}", core);
|
||||
if core != last_core {
|
||||
driver.add_measurement(meas::CurrentLoop::new(
|
||||
&name, coupling(core),
|
||||
));
|
||||
} else {
|
||||
driver.add_measurement(meas::CurrentLoop::new(
|
||||
&name, sense(core),
|
||||
));
|
||||
}
|
||||
}
|
||||
for core in 0..4 {
|
||||
for core in 0..num_cores {
|
||||
driver.add_measurement(meas::MagneticLoop::new(
|
||||
&format!("state{}", core),
|
||||
s(core),
|
||||
));
|
||||
}
|
||||
driver.add_measurement(meas::CurrentLoop::new("sense3", sense3.clone()));
|
||||
|
||||
//////// create the stimuli
|
||||
|
||||
// each row N denotes the drive currents at clock cycle N.
|
||||
// each col M denotes the drive current at core M.
|
||||
// this drive map is taken from the blog article as of 2022-08-09; it has noted errors
|
||||
// let drive_map = [
|
||||
// [1, 0, 1, 1],
|
||||
// [1, 1, 0, 1],
|
||||
// [1, 1, 1, 0],
|
||||
// [0, 1, 1, 1i32],
|
||||
// ];
|
||||
// proposed drive map: the location where a core is charged to 1 requires a negative current.
|
||||
// let drive_map = [
|
||||
// [ 1, 0, 1, 1], // clear S0 -> S1; hold S2, S3
|
||||
// [-1, 1, 0, 1], // clear S1 -> S2; hold S3; reset S0
|
||||
// [-1, 1, 1, 0], // clear S2 -> S3; hold S0 (high), S1
|
||||
// [ 0, 1, -1, 1i32], // clear S3 -> Sn; hold S1, S2 (S0 gets new input
|
||||
// ];
|
||||
// the above drive map has some implicit requirement of clock lag. it won't work with perfectly
|
||||
// square clocks. here's a realizable drive map:
|
||||
// #[allow(non_snake_case)]
|
||||
// let F = 0; // indicates a gate which has been driven to a known value and is now floating
|
||||
// #[allow(non_snake_case)]
|
||||
// let D = 0; // indicates a gate which carries data.
|
||||
// let drive_map = [
|
||||
// [-1, 1, 1, 1], // initial state: S0=1, S1=S2=S3=0
|
||||
// [-1, F, 1, 1], // open S1 for write
|
||||
|
||||
// [ 1, D, 1, 1], // clear S0 -> S1; hold S2, S3
|
||||
// [ 1, D, F, 1], // open S2 for write
|
||||
|
||||
// [-1, 1, D, 1], // clear S1 -> S2; hold S3; reset S0
|
||||
// [-1, 1, D, F], // open S3 for write
|
||||
|
||||
// [-1, 1, 1, D], // clear S2 -> S3; hold S0 (high), S1
|
||||
// [ F, 1, 1, D], // open S0 for write (not used in this demo)
|
||||
|
||||
// [ D, 1, 1, 1i32], // clear S3 -> Sn; hold S1, S2 (S0 gets new input)
|
||||
// [ D, F, 1, 1i32], // open S1 for write (not used; for completeness)
|
||||
// ];
|
||||
|
||||
use DriveType::*;
|
||||
let drive_map = [
|
||||
[HoldLow, HoldHigh, HoldHigh, HoldHigh], // initial state: S0=1, S1=S2=S3=0
|
||||
[HoldLow, ReleaseHigh, HoldHigh, HoldHigh], // open S1 for write
|
||||
|
||||
[HoldHigh, Float, HoldHigh, HoldHigh], // clear S0 -> S1; hold S2, S3
|
||||
[HoldHigh, Float, ReleaseHigh, HoldHigh], // open S2 for write
|
||||
|
||||
[HoldHigh, HoldHigh, Float, HoldHigh], // clear S1 -> S2; hold S0, S3
|
||||
// NB: bonus clock! we have to reset S0 before opening S3, else writeback from next stage
|
||||
[HoldLow, HoldHigh, Float, HoldHigh], // reset S0; hold S1, S3
|
||||
[HoldLow, HoldHigh, Float, ReleaseHigh], // open S3 for write
|
||||
|
||||
[HoldLow, HoldHigh, HoldHigh, Float], // clear S2 -> S3; hold S0 (high), S1
|
||||
[ReleaseLow, HoldHigh, HoldHigh, Float], // open S0 for write (not used in this demo)
|
||||
|
||||
[Float, HoldHigh, HoldHigh, HoldHigh], // clear S3 -> Sn; hold S1, S2 (S0 gets new input)
|
||||
[Float, ReleaseHigh, HoldHigh, HoldHigh], // open S1 for write (not used; for completeness)
|
||||
];
|
||||
|
||||
let cycles = drive_map.len() as u32;
|
||||
let duration = Seconds(clock_phase_duration * (cycles + 3) as f32);
|
||||
|
||||
for cycle in 0..cycles {
|
||||
for core in 0..4 {
|
||||
for core in 0..num_cores {
|
||||
// current polarity of the drive wires is inverted in this model v.s. the blog article,
|
||||
// so invert our currents in order to achieve the magnetic states of the article.
|
||||
let sig = drive_map[cycle as usize][core as usize];
|
||||
@@ -259,7 +249,7 @@ fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
let prefix = "out/applications/multi_core_inverter/8/";
|
||||
let prefix = "out/applications/multi_core_inverter/9/";
|
||||
let _ = std::fs::create_dir_all(&prefix);
|
||||
// driver.add_state_file(&*format!("{}state.bc", prefix), 9600);
|
||||
driver.add_serializer_renderer(&*format!("{}frame-", prefix), 400, None);
|
||||
|
Reference in New Issue
Block a user