Silicon-Chip
view release on metacpan or search on metacpan
lib/Silicon/Chip.pm view on Meta::CPAN
my sub layoutAsFiberBundle($%) # Layout the gates as a fiber bundle collapsed down to as close to the gates as possible. The returned information is sufficient to draw an svg image of the fiber bundle...
{my ($chip, %options) = @_; # Chip, options
my %gates = $chip->gates->%*; # Gates on chip
my $changed = $options{changed}; # Step at which gate last changed in simulation
my $values = $options{values}; # Values of each gate if known
my @gates = sort {$gates{$a}->seq <=> $gates{$b}->seq} keys %gates; # Gates in definition order
if (my $c = $options{changed}) # Order non IO gates by last change time during simulation if possible
{@gates = sort {($$c{$a}//0) <=> ($$c{$b}//0)} @gates;
}
my @fibers; # Squares of the page, each of which can either be undefined or contain the name of the fiber crossing it from left to right or up and down
my @inPlay; # Squares of the page in play
my @positions; # Position of each gate indexed by position in layout
my %positions; # Position of each gate indexed by gate name
my $width = 1; # Width of page consumed so far until it becomes the page width.
my $height = 0; # Height of page consumed so far until it becomes the page height
for my $i(keys @gates) # Position each gate
{my $g = $gates{$gates[$i]}; # Gate details
my $s = $g->type =~ m(\A(input|one|output|zero)\Z); # These gates can be positioned without consuming more horizontal space
my %i = $g->inputs->%*; # Inputs hash for gate
my @i = sort keys %i; # Connections to each gate in pin order
my $w = $s ? 1 : scalar(@i); # Width of this gate
my $n = $g->output; # Name of gate
my sub color() # Color of gate
{return "red" if $g->io == gateOuterOutput;
return "blue" if $g->io == gateOuterInput;
"green"
}
my $x = $width; $x-- if $s; # Position of gate
my $y = $i;
my $p = genHash(__PACKAGE__."::GatePosition",
output => $g->output, # Gate name
x => $x, # Gate x position
y => $y, # Gate y position
width => $w, # Width of gate
fiber => 0, # Number of fibers running past this gate
position => $i, # Number of fibers running past this gate
type => $g->type, # Type of gate
value => $$values {$g->output}, # Value of gate if known
changed => $$changed{$g->output}, # Last change time of gate if known
inputs => [map {$i{$_}} @i], # Names of gates driving input pins on this gate
inputValues => [map {$$values{$i{$_}}} @i], # Values on input pins if known
color => color, # Color of gate
inPin => $g->io == gateOuterInput, # Input pin for chip
outPin => $g->io == gateOuterOutput, # Output pin for chip
);
$positions[$i] = $p; $positions{$p->output} = $p; # Index the gates
$width += $w unless $s; # Io gates are tucked in in such way that they do not contribute to the width
$height++ unless $g->io == gateOuterOutput; # Output gates do not contribute to the height of the mask
}
for my $i(keys @positions) # Position output pins along bottom of mask
{my $p = $positions[$i];
next unless $p->outPin;
my ($D) = $p->inputs->@*; # An output gate only has one input so we can safe relocate it next to the single gate that produces that output
my $d = $positions{$D}; # Driving gate
$p->x = $d->x - 1; # Reposition output gate
$p->y = $d->y;
}
for my $p(@positions) # Connect gates loosely
{my $g = $gates{$p->output}; # Detail for this gate
my @i = $p->inputs->@*; # Connections to each gate
for my $i(keys @i) # Connections to each gate
{my $D = $i[$i]; # Driving gate name
my $d = $positions{$D}; # Driving gate position
my $X = $p->x+$i; # X position of input pin to gate
my $Y = $p->y; # Y position of input pin to gate
$fibers[$_][$d->y][0] = $D for $d->x+$d->width..$X; # Horizontal line
$fibers[$X][$_] [1] = $D for $d->y..$Y-1; # Vertical line
if (!$g->io) # Mark column as in play
{for my $j(0..$Y-1)
{$inPlay[$X][$j] = 1;
}
}
}
}
my sub collapseFibers() # Perform one collapse pass of the fibers returning the number of collapses performed
{my $changes = 0; # Number of changes made in this pass
my sub removeOrphans($) # Remove any vertical orphans in the specified column
{my ($i) = @_; # Column to check
for my $j(keys $fibers[$i]->@*)
{my $h = $fibers[$i][$j][0]; # Horizontal line
my $v = $fibers[$i][$j][1]; # Vertical line
last if defined($h) and defined($v) and $h eq $v; # Found the vertical so we can stop
$fibers[$i][$j][1] = undef; # Remove vertical as it never meets a corresponding horizontal and so is of no use
}
}
for my $i(keys @fibers) # Examine each cell for a corner that we can collapse either left or down
{for my $j(keys $fibers[$i]->@*)
{my sub i() {$i}
my sub j() {$j}
my sub h($$) :lvalue {my ($i, $j) = @_; return undef unless $i >= 0 and $j >= 0 and $inPlay[$i][$j]; $fibers[$i][$j][0]} # A horizontal element relative to the current corner
my sub v($$) :lvalue {my ($i, $j) = @_; return undef unless $i >= 0 and $j >= 0 and $inPlay[$i][$j]; $fibers[$i][$j][1]} # A vertical element relative to the current corner
my $a = h(i-1, j+0); my sub a() {$a}
my $b = h(i+0, j+0);
my $B = v(i+0, j+0);
my $C = v(i+0, j+1);
my $D = v(i+0, j-1);
my $e = h(i+1, j+0);
next unless defined($a) and defined($b) and defined($B) and defined($C);# Possible corner
next unless $a eq $b and $b eq $B and $B eq $C; # Confirm corner
next if defined($D) and $D eq $a; # If it is a corner it points north east.
next if defined($e) and $e eq $a; # If it is a corner it points north east.
my $wentLeft; # If we collapsed left we made a change and so need to come around again before attempting to collapse down
if (1) # Collapse left
{my $k; my sub k() :lvalue {$k} # Position of new corner going left
for my $I(reverse 0..i-1) # Look for an opposite corner
{last if $j+2 >= $fibers[$I]->$#*;
last unless defined(h($I, j)) and h($I, j) eq $a; # Make sure horizontal is occupied with expected bus line
( run in 0.568 second using v1.01-cache-2.11-cpan-5511b514fd6 )