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:
parent
59a4419130
commit
652621e47a
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue