app: multi_core_inverter: more precise clock management

try to control the edges when the clock is release to prevent ringing.
This commit is contained in:
colin 2022-08-10 16:39:56 -07:00
parent 59a4419130
commit 652621e47a
1 changed files with 95 additions and 32 deletions

View File

@ -42,13 +42,46 @@ use coremem::meas;
use coremem::real::{self, Real as _};
use coremem::sim::spirv::{self, SpirvSim};
use coremem::sim::units::Seconds;
use coremem::stim::{CurlStimulus, Gated, Sinusoid1, TimeVarying as _};
use coremem::stim::{CurlStimulus, Exp, Gated, Sinusoid1, TimeVarying as _};
use coremem::Driver;
type R = real::R32;
type Mat = IsoConductorOr<R, Ferroxcube3R1MH>;
// type Backend = spirv::CpuBackend;
type Backend = spirv::WgpuBackend;
type Sim = SpirvSim::<R, Mat, Backend>;
#[derive(Clone, Copy)]
enum DriveType {
HoldHigh,
ReleaseHigh,
HoldLow,
ReleaseLow,
Float,
}
impl DriveType {
fn direction(&self) -> i32 {
use DriveType::*;
match self {
HoldHigh | ReleaseHigh => 1,
HoldLow | ReleaseLow => -1,
Float => 0,
}
}
fn is_hold(&self) -> bool {
match self {
Self::HoldHigh | Self::HoldLow => true,
_ => false,
}
}
fn is_release(&self) -> bool {
match self {
Self::ReleaseHigh | Self::ReleaseLow => true,
_ => false,
}
}
}
fn main() {
coremem::init_logging();
@ -59,7 +92,7 @@ fn main() {
let feat_size = um(10);
let input_magnitude = 1.0e9;
let clock_phase_duration = ps(1000);
let clock_phase_duration = ps(2000);
let s_major = um(160);
let s_minor = um(30);
@ -83,26 +116,39 @@ fn main() {
// coupling(n) is the wire which couples core n into core n+1
let coupling = |n| Torus::new_xz(Meters::new(couplingx(n), sy, sz), coupling_major, coupling_minor);
let input = |region: &Torus, cycle: u32, direction: i32| {
let control_signal = |driver: &mut Driver<Sim>, region: &Torus, cycle: u32, ty: DriveType| {
let area = region.cross_section();
let amp = direction as f32 * input_magnitude / area;
// negation because blog confuses current directions.
let amp = -ty.direction() as f32 * input_magnitude / area;
let start = clock_phase_duration * cycle as f32;
let wave = Gated::new(amp, start, start + clock_phase_duration);
// TODO: square waves create harmonics which cause unwanted state changes. use a round clk.
if ty.is_hold() {
// simple square wave:
let wave = Gated::new(amp, start, start + clock_phase_duration);
let stim = CurlStimulus::new(
region.clone(),
wave.clone(),
);
driver.add_stimulus(stim);
} else if ty.is_release() {
// decaying exponential wave:
let wave = Exp::new_at(amp, start, 0.125 * clock_phase_duration /* half-life */);
let stim = CurlStimulus::new(
region.clone(),
wave.clone(),
);
driver.add_stimulus(stim);
} // else: Float
// sine wave (untested):
// let wave = Sinusoid1::from_wavelength(direction as f32 * input_magnitude / area, clock_phase_duration * 2.0)
// .half_cycle()
// .shifted(clock_phase_duration * cycle as f32);
CurlStimulus::new(
region.clone(),
wave.clone(),
)
};
let wire_mat = IsomorphicConductor::new(1e6f32.cast::<R>());
// let ferro_mat = wire_mat;
let ferro_mat = Ferroxcube3R1MH::new();
let mut driver = Driver::new(SpirvSim::<R, Mat, Backend>::new(
let mut driver = Driver::new(Sim::new(
sim_bounds.to_index(feat_size), feat_size,
));
driver.add_classical_boundary_explicit::<R, _>(sim_padding);
@ -160,43 +206,60 @@ fn main() {
// ];
// 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.
// #[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 = [
[-1, 1, 1, 1], // initial state: S0=1, S1=S2=S3=0
[-1, F, 1, 1], // open S1 for write
[HoldLow, HoldHigh, HoldHigh, HoldHigh], // initial state: S0=1, S1=S2=S3=0
[HoldLow, ReleaseHigh, HoldHigh, HoldHigh], // open S1 for write
[ 1, D, 1, 1], // clear S0 -> S1; hold S2, S3
[ 1, D, F, 1], // open S2 for write
[HoldHigh, Float, HoldHigh, HoldHigh], // clear S0 -> S1; hold S2, S3
[HoldHigh, Float, ReleaseHigh, HoldHigh], // open S2 for write
[-1, 1, D, 1], // clear S1 -> S2; hold S3; reset S0
[-1, 1, D, F], // open S3 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
[-1, 1, 1, D], // clear S2 -> S3; hold S0 (high), S1
[ F, 1, 1, D], // open S0 for write (not used in this demo)
[HoldLow, HoldHigh, HoldHigh, Float], // clear S2 -> S3; hold S0 (high), S1
[ReleaseLow, HoldHigh, HoldHigh, Float], // 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)
[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 + 2) as f32);
let duration = Seconds(clock_phase_duration * (cycles + 3) as f32);
for cycle in 0..cycles {
for core in 0..4 {
// 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 dir = -drive_map[cycle as usize][core as usize];
if dir != 0 {
// micro opt/safety: don't place zero-magnitude stimuli
driver.add_stimulus(input(&ctl(core), cycle, dir));
}
let sig = drive_map[cycle as usize][core as usize];
control_signal(&mut driver, &ctl(core), cycle, sig);
}
}
let prefix = "out/applications/multi_core_inverter/6/";
let prefix = "out/applications/multi_core_inverter/8/";
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);