velxio/frontend/src/__tests__/chipbus-buslogic.test.ts

103 lines
4.0 KiB
TypeScript

/**
* Phase 1 — 4-valued logic + drive-strength resolution (project/multichip-bus/).
* Covers the exit criteria from 03-phases.md: a shared net with a pull-up and
* two tri-state drivers resolves to 0/1/Z/X per the rules; contention -> X.
*/
import { describe, it, expect } from 'vitest';
import {
resolveNet,
modeToDrive,
resolvedToBool,
Strength,
HIGHZ_DRIVE,
type Drive,
} from '../simulation/customChips/busLogic';
const strong = (v: 0 | 1): Drive => ({ value: v, strength: Strength.STRONG });
const pull = (v: 0 | 1): Drive => ({ value: v, strength: Strength.PULL });
const supply = (v: 0 | 1): Drive => ({ value: v, strength: Strength.SUPPLY });
describe('busLogic — resolveNet', () => {
it('no drivers -> Z (floating)', () => {
expect(resolveNet([]).v).toBe('Z');
expect(resolveNet([HIGHZ_DRIVE, HIGHZ_DRIVE]).v).toBe('Z');
});
it('a single strong driver wins', () => {
expect(resolveNet([strong(1)]).v).toBe('1');
expect(resolveNet([strong(0)]).v).toBe('0');
});
it('any real driver overrides Z', () => {
expect(resolveNet([strong(1), HIGHZ_DRIVE, HIGHZ_DRIVE]).v).toBe('1');
});
it('strong beats pull', () => {
expect(resolveNet([strong(0), pull(1)]).v).toBe('0');
expect(resolveNet([pull(0), strong(1)]).v).toBe('1');
});
it('a lone pull resistor wins over all-Z (floating bus reads the pull)', () => {
expect(resolveNet([pull(1), HIGHZ_DRIVE, HIGHZ_DRIVE]).v).toBe('1');
expect(resolveNet([pull(0)]).v).toBe('0');
});
it('supply beats strong', () => {
expect(resolveNet([supply(1), strong(0)]).v).toBe('1');
});
it('two strong drivers that agree -> that value (no contention)', () => {
expect(resolveNet([strong(1), strong(1)]).v).toBe('1');
});
it('two strong drivers that disagree -> X (contention)', () => {
const r = resolveNet([strong(0), strong(1)]);
expect(r.v).toBe('X');
expect(r.strength).toBe(Strength.STRONG);
});
it('reports the winning strength', () => {
expect(resolveNet([pull(1)]).strength).toBe(Strength.PULL);
expect(resolveNet([strong(1)]).strength).toBe(Strength.STRONG);
expect(resolveNet([]).strength).toBe(Strength.HIGHZ);
});
// The Phase 1 exit scenario: a data bus with a pull-up + two tri-state drivers.
it('tri-state bus: only the enabled driver drives; releasing falls to the pull-up', () => {
const pullup = pull(1);
// Driver A enabled to 0, driver B released -> bus reads 0.
expect(resolveNet([pullup, strong(0), HIGHZ_DRIVE]).v).toBe('0');
// Both drivers released -> bus floats up to the pull-up -> 1.
expect(resolveNet([pullup, HIGHZ_DRIVE, HIGHZ_DRIVE]).v).toBe('1');
// Both drivers enabled to OPPOSITE values -> contention -> X.
expect(resolveNet([pullup, strong(0), strong(1)]).v).toBe('X');
});
});
describe('busLogic — modeToDrive', () => {
it('outputs drive strong at their value', () => {
expect(modeToDrive(1 /* OUTPUT */, 1)).toEqual({ value: 1, strength: Strength.STRONG });
expect(modeToDrive(16 /* OUTPUT_LOW */, 0)).toEqual({ value: 0, strength: Strength.STRONG });
expect(modeToDrive(17 /* OUTPUT_HIGH */, 1)).toEqual({ value: 1, strength: Strength.STRONG });
});
it('plain input / analog release the bus (Hi-Z)', () => {
expect(modeToDrive(0 /* INPUT */, 1).strength).toBe(Strength.HIGHZ);
expect(modeToDrive(4 /* ANALOG */, 0).strength).toBe(Strength.HIGHZ);
});
it('pull-up / pull-down contribute a pull-strength level', () => {
expect(modeToDrive(2 /* INPUT_PULLUP */, 0)).toEqual({ value: 1, strength: Strength.PULL });
expect(modeToDrive(3 /* INPUT_PULLDOWN */, 1)).toEqual({ value: 0, strength: Strength.PULL });
});
});
describe('busLogic — resolvedToBool', () => {
it('only a driven 1 is high; 0/Z/X read low', () => {
expect(resolvedToBool(resolveNet([strong(1)]))).toBe(true);
expect(resolvedToBool(resolveNet([strong(0)]))).toBe(false);
expect(resolvedToBool(resolveNet([]))).toBe(false); // Z
expect(resolvedToBool(resolveNet([strong(0), strong(1)]))).toBe(false); // X
});
});