Initial Commit
This commit is contained in:
commit
8b0e867731
5
old/README.md
Normal file
5
old/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# rcircuit
|
||||
This is an HDL language for ruby.
|
||||
|
||||
The braching system used is [OneFlow](http://endoflineblog.com/oneflow-a-git-branching-model-and-workflow), with one change:
|
||||
When a branch is integrated with master, it is simply merged.
|
182
old/kt8.rb
Executable file
182
old/kt8.rb
Executable file
@ -0,0 +1,182 @@
|
||||
|
||||
require_relative "rcircuit_lib"
|
||||
|
||||
|
||||
class ALU < Device
|
||||
def initialize(init_args={},width)
|
||||
#define inputs and outputs
|
||||
define_port('a', width)
|
||||
define_port('b', width)
|
||||
define_port('op', 4)
|
||||
define_port('out', width)
|
||||
#connect ports in init arguments
|
||||
init_assign(init_args)
|
||||
#create internal components
|
||||
define_device("mux", Mux.new(8, 4).sel(op)
|
||||
.in0(a + b)
|
||||
.in1(a - b)
|
||||
.in2(a & b)
|
||||
.in3(a | b)
|
||||
.in4(a ^ b)
|
||||
.in5(!a)
|
||||
.in6(!b)
|
||||
.in7(a)
|
||||
.in8(b)
|
||||
.in9(a << 1)
|
||||
.in10(a >> 1)
|
||||
.in11(a + 1)
|
||||
.in12(a - 1)
|
||||
.in13(0)
|
||||
.in14(0)
|
||||
.in15(0) )
|
||||
mux.out.connect(out)
|
||||
end
|
||||
end
|
||||
|
||||
def ALU.test
|
||||
puts "ALU test:"
|
||||
a=Port.new(8)
|
||||
b=Port.new(8)
|
||||
op=Port.new(4)
|
||||
alu=ALU.new({"a"=>a,"b"=>b,"op"=>op},8)
|
||||
dbg=Dbg.new({"a"=>a,"b"=>b,"op"=>op,"out"=>alu.out},true)
|
||||
a.value=3
|
||||
b.value=10
|
||||
dbg.add_trigger("op")
|
||||
for i in (0..15) do
|
||||
op.value=i
|
||||
end
|
||||
end
|
||||
class KT8Decoder < Device
|
||||
def initialize(init_args={})
|
||||
define_port("ins",8)
|
||||
init_assign(init_args)
|
||||
define_port("aen")
|
||||
define_port("ben")
|
||||
define_port("ren")
|
||||
define_port("wr")
|
||||
define_port("rbsel")
|
||||
define_port("den")
|
||||
@ins4to7=ins.slice(4..7)
|
||||
@ins0to3=ins.slice(0..3)
|
||||
define_device("dec", Decoder.new(4)
|
||||
.sel(@ins4to7)
|
||||
.en(1)
|
||||
.o0(aen)
|
||||
.o1(ben)
|
||||
.o2(wr)
|
||||
.o3(rbsel)
|
||||
.o8(ren)
|
||||
.o11(den) )
|
||||
define_device("dec1", Decoder.new(4).sel(@ins0to3).en(den) )
|
||||
end
|
||||
def on_change(new_val)
|
||||
puts "ins:#{ins.value},ins[5:7]:#{@ins5to7.value},ins[4:7]:#{@ins4to7.value}"
|
||||
if @ins5to7.value==0
|
||||
puts "5to7=0"
|
||||
aen.value=1
|
||||
ben.value=0
|
||||
ren.value=0
|
||||
wr.value=0
|
||||
rbsel.value=0
|
||||
end
|
||||
if @ins5to7.value==1
|
||||
puts "5to7=1"
|
||||
aen.value=0
|
||||
ben.value=1
|
||||
ren.value=0
|
||||
wr.value=0
|
||||
rbsel.value=0
|
||||
end
|
||||
if @ins5to7.value==2
|
||||
puts "5to7=2"
|
||||
aen.value=0
|
||||
ben.value=0
|
||||
ren.value=0
|
||||
wr.value=1
|
||||
rbsel.value=0
|
||||
end
|
||||
if @ins4to7.value==6
|
||||
puts "4to7=6"
|
||||
aen.value=0
|
||||
ben.value=0
|
||||
ren.value=0
|
||||
wr.value=0
|
||||
rbsel.value=1
|
||||
end
|
||||
if @ins4to7.value==7
|
||||
puts "4to7=7"
|
||||
aen.value=0
|
||||
ben.value=0
|
||||
ren.value=0
|
||||
wr.value=0
|
||||
rbsel.value=2
|
||||
end
|
||||
if @ins4to7.value==8
|
||||
puts "4to7=8"
|
||||
aen.value=0
|
||||
ben.value=0
|
||||
ren.value=1
|
||||
wr.value=0
|
||||
rbsel.value=0
|
||||
end
|
||||
end
|
||||
end
|
||||
#start of KT8 parts
|
||||
class KT8 < Device
|
||||
def initialize(init_args={})
|
||||
define_port("clk")
|
||||
define_port("rst")
|
||||
define_port("dout",8)
|
||||
define_port("ins",8)
|
||||
init_assign(init_args)
|
||||
define_port("wr")
|
||||
define_port("paddr",8)
|
||||
define_port("daddr",5)
|
||||
define_port("din",8)
|
||||
define_device("dec", KT8Decoder.new().ins(ins).wr(wr))
|
||||
define_device("pc", Counter.new(8).clk(clk).rst(rst).load(0).out(paddr))
|
||||
define_device("reg_a", Reg.new(8).clk(clk).rst(rst).en(dec.aen))
|
||||
define_device("reg_b",Reg.new(8).clk(clk).rst(rst).en(dec.ben))
|
||||
define_device("reg_r",Reg.new(8).clk(clk).rst(rst).en(dec.ren))
|
||||
define_device("alu",ALU.new(8).a(reg_a.out).b(reg_b.out).op(ins.slice(0..3)).out(reg_r.in))
|
||||
daddr.connect(ins.slice(0..4))
|
||||
din.connect(reg_r.out)
|
||||
#reg A input is always from data memory
|
||||
reg_a.in(dout)
|
||||
#reg B can be from memory or current value combined with immediate bits
|
||||
low_bit_combine = reg_b.out.slice(4..7).join(ins.slice(0..3))
|
||||
high_bit_combine = ins.out.slice(0..3).join(reg_b.out.slice(0..3))
|
||||
reg_b_source = Mux.new(8,1).in0(dout).in1(low_bit_combine).sel(dec.rbsel)
|
||||
reg_b.in(reg_b_source.out)
|
||||
end
|
||||
end
|
||||
|
||||
clk=Port.new
|
||||
rst=Port.new
|
||||
prog_mem=Ram.new(8,8)
|
||||
data_mem=Ram.new(8,5)
|
||||
kt8=KT8.new("clk"=>clk,"rst"=>rst,"dout"=>data_mem.out,"ins"=>prog_mem.out)
|
||||
prog_mem.addr(kt8.paddr).clk(clk).wr(0).in(0)
|
||||
data_mem.addr(kt8.daddr).clk(clk).wr(kt8.wr).in(kt8.din)
|
||||
dbg=Dbg.new("paddr"=>kt8.paddr,"daddr"=>kt8.daddr,"aval"=>kt8.reg_a.out,"rval"=>kt8.reg_r.out,"ins"=>kt8.ins,"aen"=>kt8.dec.aen,"ben"=>kt8.dec.ben,"ren"=>kt8.dec.ren,"wr"=>kt8.dec.wr,"rbsel"=>kt8.dec.rbsel,"aluop"=>prog_mem.out.slice(0..3))
|
||||
prog_mem[0]="00000000"
|
||||
prog_mem[1]="10000111"
|
||||
prog_mem[2]="01000001"
|
||||
data_mem[0]="1011"
|
||||
rst.value=0
|
||||
clk.value=0
|
||||
rst.value=1
|
||||
rst.value=0
|
||||
dbg.out
|
||||
clk.value=1
|
||||
dbg.out
|
||||
clk.value=0
|
||||
clk.value=1
|
||||
dbg.out
|
||||
clk.value=0
|
||||
clk.value=1
|
||||
dbg.out
|
||||
|
||||
puts "Putting.."
|
||||
puts data_mem[1].inspect
|
223
old/net.rb
Executable file
223
old/net.rb
Executable file
@ -0,0 +1,223 @@
|
||||
require 'set'
|
||||
#endpoints for nets
|
||||
class NetPort
|
||||
|
||||
def initialize(width=1)
|
||||
@callbacks = []
|
||||
@updating = false
|
||||
#set up a new net with a matching width
|
||||
Net.new(self, width)
|
||||
end
|
||||
|
||||
def connect(other)
|
||||
if other.is_defined?
|
||||
self.value = other.value #drive into the new net first
|
||||
end
|
||||
Net.get_net(self).connect(other)
|
||||
self #return self for call chaining
|
||||
end
|
||||
|
||||
def value
|
||||
Net.get_net(self).value
|
||||
end
|
||||
def bvalue
|
||||
unless Net.get_net(self).value==nil
|
||||
Net.get_net(self).value.to_s(2)
|
||||
end
|
||||
end
|
||||
def width
|
||||
Net.get_net(self).width
|
||||
end
|
||||
|
||||
def value=(new_value)
|
||||
Net.get_net(self).drive(new_value)
|
||||
end
|
||||
|
||||
def posedge?
|
||||
Net.get_net(self).posedge?
|
||||
end
|
||||
|
||||
def negedge?
|
||||
Net.get_net(self).negedge?
|
||||
end
|
||||
|
||||
def is_defined?
|
||||
self.value != nil
|
||||
end
|
||||
|
||||
def undefine
|
||||
self.value = nil
|
||||
end
|
||||
|
||||
#called by Net, do not call directly
|
||||
def _update(value)
|
||||
if @updating
|
||||
raise RuntimeError, "Signal loop detected in #{self.get_name}"
|
||||
end
|
||||
@updating = true
|
||||
#send new value to each listener
|
||||
@callbacks.each do |callback|
|
||||
callback.call(value)
|
||||
end
|
||||
@updating = false
|
||||
end
|
||||
|
||||
def add_callback(&callback)
|
||||
#add block to the list, put at head
|
||||
@callbacks.insert(0, callback)
|
||||
self #return self for call chaining
|
||||
end
|
||||
|
||||
def add_late_callback(&callback)
|
||||
#add block to the list, put at end to be called last
|
||||
@callbacks.push(callback)
|
||||
self #return self for call chaining
|
||||
end
|
||||
|
||||
def set_name(name)
|
||||
@name = name
|
||||
end
|
||||
|
||||
def set_parent(parent)
|
||||
@parent = parent
|
||||
end
|
||||
|
||||
def get_name
|
||||
if @name == nil
|
||||
@name = self.class
|
||||
end
|
||||
if @parent != nil
|
||||
@parent.get_name + "." + @name
|
||||
else
|
||||
@name
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class Net
|
||||
@@assignments = {} #table of nets assigned to ports
|
||||
@@propagate_list = [] #nets that have changed during propagate call
|
||||
@@propagate_flag = false
|
||||
|
||||
def initialize(port, width=1)
|
||||
if @@assignments.has_key?(port)
|
||||
raise ArgumentError, "Repeat assignment of port to new net"
|
||||
end
|
||||
@ports = Set.new
|
||||
@ports.add(port)
|
||||
@width = width
|
||||
@max_value = 2**@width - 1
|
||||
@value = nil
|
||||
@posedge = false
|
||||
@negedge = false
|
||||
@@assignments[port] = self
|
||||
end
|
||||
|
||||
def ports
|
||||
@ports
|
||||
end
|
||||
|
||||
def width
|
||||
@width
|
||||
end
|
||||
|
||||
def connect(other)
|
||||
if @width != other.width
|
||||
raise ArgumentError, "Cannot connect nets of different widths"
|
||||
end
|
||||
if @@assignments.has_key?(other)
|
||||
#net already assigned to other port.
|
||||
#Merge them and update @@assignments table
|
||||
other_net = @@assignments[other]
|
||||
@ports.merge(other_net.ports)
|
||||
other_net.ports.each { |port_in_other| @@assignments[port_in_other] = self }
|
||||
else
|
||||
#unassigned port, just add it
|
||||
@ports.add(other)
|
||||
@@assignments[other] = self
|
||||
end
|
||||
end
|
||||
|
||||
def drive(new_value)
|
||||
if new_value.is_a?(NetPort)
|
||||
new_value=new_value.value
|
||||
end
|
||||
if new_value != nil && (new_value < 0 || new_value > @max_value)
|
||||
raise ArgumentError, "Invalid value (#{new_value}) for net"
|
||||
end
|
||||
if new_value != @value
|
||||
@new_value = new_value
|
||||
@@propagate_list.push(self) #schedule to run update
|
||||
if not @@propagate_flag
|
||||
#automatically propagate the change and all changes that result
|
||||
@@propagate_flag = true #prevent recursion
|
||||
Net.propagate
|
||||
@@propagate_flag = false #unlock for next time
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#loops until all updates complete
|
||||
def self.propagate
|
||||
#propagate list will grow as downstream nets are changed
|
||||
#keep updating until none are left
|
||||
while @@propagate_list.length > 0 do
|
||||
net = @@propagate_list.shift
|
||||
net.update
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
@posedge = (@value == 0 && @new_value != nil && @new_value > 0)
|
||||
@negedge = (@value != nil && @value > 0 && @new_value == 0)
|
||||
@value = @new_value
|
||||
@ports.each { |driven| driven._update(@new_value) }
|
||||
#edges only last for the current update
|
||||
@posedge = false
|
||||
@negedge = false
|
||||
end
|
||||
|
||||
def value
|
||||
@value
|
||||
end
|
||||
|
||||
def posedge?
|
||||
@posedge
|
||||
end
|
||||
|
||||
def negedge?
|
||||
@negedge
|
||||
end
|
||||
|
||||
def value=(new_value)
|
||||
drive(new_value)
|
||||
end
|
||||
|
||||
#get net currently assigned to the given port (or a new one)
|
||||
def self.get_net(port)
|
||||
if @@assignments.has_key?(port)
|
||||
@@assignments[port]
|
||||
else
|
||||
self.new(port)
|
||||
end
|
||||
end
|
||||
|
||||
def self.test
|
||||
a = NetPort.new(4).add_callback { |value| puts "A set to #{value}" }
|
||||
b = NetPort.new(4).add_callback { |value| puts "B set to #{value}" }
|
||||
c = NetPort.new(4).add_callback { |value| puts "C set to #{value}" }
|
||||
d = NetPort.new(4).add_callback { |value| puts "D set to #{value}" }
|
||||
|
||||
#connect A-B and C-D
|
||||
a.connect(b)
|
||||
c.connect(d)
|
||||
|
||||
a.value = 1
|
||||
c.value = 2
|
||||
#connect them all together
|
||||
b.connect(c)
|
||||
a.value = 3
|
||||
d.value = 4
|
||||
end
|
||||
end
|
15
old/rcircuit_devices.txt
Executable file
15
old/rcircuit_devices.txt
Executable file
@ -0,0 +1,15 @@
|
||||
Reg:
|
||||
Inputs:in,clk,en,rst
|
||||
Outputs:out
|
||||
Counter:
|
||||
Inputs:in,clk,load,rst
|
||||
Outputs:out
|
||||
Mux:
|
||||
Inputs:in0,in1...
|
||||
Outputs:out
|
||||
Decoder:
|
||||
Inputs:sel
|
||||
Outputs:o0,o1...
|
||||
Ram:
|
||||
Inputs:in,clk,wr
|
||||
Outputs:out
|
837
old/rcircuit_lib.rb
Executable file
837
old/rcircuit_lib.rb
Executable file
@ -0,0 +1,837 @@
|
||||
# Ruby Circuit Simulator
|
||||
# Library file
|
||||
|
||||
# holds a digital value
|
||||
# can call listener callbacks when value changes
|
||||
|
||||
require_relative "net"
|
||||
#add some extra functionality to the basic NetPort
|
||||
class Port < NetPort
|
||||
include Comparable
|
||||
|
||||
#override to handle bitstrings
|
||||
def value=(new_value)
|
||||
if new_value.class == String
|
||||
if new_value.length != self.width
|
||||
raise ArgumentError, "Wrong width for bitstring: #{new_value}"
|
||||
end
|
||||
numval = 0
|
||||
new_value.reverse.each do |bit|
|
||||
numval = numval << 1
|
||||
if bit == "1"
|
||||
numval += 1
|
||||
elsif bit != "0"
|
||||
raise ArgumentError, "String values (#{new_value}) must be binary"
|
||||
end
|
||||
end
|
||||
super(numval)
|
||||
else
|
||||
super(new_value)
|
||||
end
|
||||
end
|
||||
|
||||
def convert_port(otherval)
|
||||
#checks if argument is a port or number
|
||||
#if it is a number, convert it to a contant port
|
||||
if otherval.class == Integer
|
||||
return PortConstant.new(self.width, otherval)
|
||||
else
|
||||
return otherval
|
||||
end
|
||||
end
|
||||
|
||||
def &(other)
|
||||
other = convert_port(other)
|
||||
return AndGate.new(self,other).out
|
||||
end
|
||||
|
||||
def |(other)
|
||||
other = convert_port(other)
|
||||
return OrGate.new(self,other).out
|
||||
end
|
||||
|
||||
def ^(other)
|
||||
other = convert_port(other)
|
||||
return XorGate.new(self, other).out
|
||||
end
|
||||
|
||||
def !
|
||||
return NotGate.new(self).out
|
||||
end
|
||||
|
||||
def +(other)
|
||||
other = convert_port(other)
|
||||
return Adder.new(self.width,{"a"=>self,"b"=>other}).out
|
||||
end
|
||||
|
||||
def -(other)
|
||||
other = convert_port(other)
|
||||
return Subtractor.new(self.width,{"a"=>self,"b"=>other}).out
|
||||
end
|
||||
|
||||
def slice(index)
|
||||
if index.class==Integer
|
||||
port=Port.new(1) #single line
|
||||
mask = 1 << index
|
||||
shift = index
|
||||
else
|
||||
port=Port.new(index.size) #range
|
||||
mask = (2**(index.size) - 1) << index.first
|
||||
shift = index.first
|
||||
end
|
||||
#copies slice to new port
|
||||
self.add_callback do |new_value|
|
||||
port.value = (new_value & mask) >> shift
|
||||
end
|
||||
return port
|
||||
end
|
||||
|
||||
def join(other,last=false)
|
||||
port=Port.new(self.width+other.width)
|
||||
def update_port(last,port,other)
|
||||
if self.is_defined? && other.is_defined?
|
||||
if last
|
||||
port.value=(other.value << self.width) + self.value
|
||||
else
|
||||
port.value=(self.value << other.width) + other.value
|
||||
end
|
||||
else
|
||||
port.undefine
|
||||
end
|
||||
end
|
||||
self.add_callback {|value| update_port(last,port,other)}
|
||||
other.add_callback {|value| update_port(last,port,other)}
|
||||
return port
|
||||
end
|
||||
|
||||
def <=>(other)
|
||||
other = convert_port(other)
|
||||
return self.value<=>other.value
|
||||
end
|
||||
|
||||
def >>(shiftbits)
|
||||
return LSR.new(self.width, shiftbits).in(self).out
|
||||
end
|
||||
|
||||
def <<(shiftbits)
|
||||
return LSL.new(self.width, shiftbits).in(self).out
|
||||
end
|
||||
|
||||
def bitstring
|
||||
if is_defined?
|
||||
mask = 1 << (self.width - 1)
|
||||
strval = ""
|
||||
self.width.times do
|
||||
if (self.value & mask) > 0
|
||||
strval += "1"
|
||||
else
|
||||
strval += "0"
|
||||
end
|
||||
mask = mask >> 1
|
||||
end
|
||||
strval
|
||||
else
|
||||
"X"*self.width
|
||||
end
|
||||
end
|
||||
|
||||
def method_missing(m, *args, &block)
|
||||
return self
|
||||
end
|
||||
end
|
||||
|
||||
class PortConstant < Port
|
||||
def initialize(width, value)
|
||||
super(width)
|
||||
@assigned = false
|
||||
self.value = value
|
||||
@assigned = true
|
||||
end
|
||||
|
||||
def _update(newvalue)
|
||||
if @assigned
|
||||
#not initial assigment
|
||||
#raise RuntimeError, "Cannot change value of constant port"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class Gate
|
||||
def initialize(*args)
|
||||
@inputs = []
|
||||
if args.length==0
|
||||
@width=1
|
||||
@out = Port.new(@width)
|
||||
elsif args[0].class==Integer
|
||||
@width=args.shift
|
||||
@out = Port.new(@width)
|
||||
args.each do |input|
|
||||
add_input(input)
|
||||
end
|
||||
else
|
||||
@width=args[0].width
|
||||
@out = Port.new(@width)
|
||||
args.each do |input|
|
||||
add_input(input)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def add_input(input_port)
|
||||
if input_port.width != @width then
|
||||
raise ArgumentError, "Incorrect port width"
|
||||
end
|
||||
@inputs.push(input_port)
|
||||
input_port.add_callback {|value| self.input_changed(value)}
|
||||
input_changed(input_port.value)
|
||||
end
|
||||
|
||||
def out
|
||||
return @out
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
class NotGate < Gate
|
||||
def initialize(*args)
|
||||
super
|
||||
@outmask = (2**@width)-1
|
||||
end
|
||||
|
||||
def add_input(input_port)
|
||||
if @inputs.length > 0 then
|
||||
raise ArgumentError, "Cannot add multiple inputs to NotGate"
|
||||
end
|
||||
super
|
||||
end
|
||||
|
||||
alias set_input add_input
|
||||
|
||||
def input_changed(new_value)
|
||||
inport=@inputs[0]
|
||||
if inport.is_defined?
|
||||
out.value = (~inport.value) & @outmask
|
||||
else
|
||||
out.undefine
|
||||
end
|
||||
end
|
||||
|
||||
def self.test
|
||||
puts "NOT Test:"
|
||||
nin = Port.new()
|
||||
not_gate = NotGate.new(nin)
|
||||
dbg = Dbg.new( {"in"=>nin, "out"=>not_gate.out})
|
||||
dbg.out
|
||||
nin.value = 0
|
||||
dbg.out
|
||||
nin.value = 1
|
||||
dbg.out
|
||||
end
|
||||
end
|
||||
|
||||
class AndGate < Gate
|
||||
|
||||
def input_changed(new_value)
|
||||
andval = nil
|
||||
@inputs.each do |inport|
|
||||
if inport.is_defined?
|
||||
if andval == nil
|
||||
andval = inport.value
|
||||
else
|
||||
andval = andval & inport.value
|
||||
end
|
||||
else
|
||||
out.undefine
|
||||
return
|
||||
end
|
||||
end
|
||||
out.value = andval
|
||||
end
|
||||
|
||||
def self.test()
|
||||
puts "AND Test:"
|
||||
in_a = Port.new()
|
||||
in_b = Port.new()
|
||||
and_gate = AndGate.new(in_a, in_b)
|
||||
dbg = Dbg.new( {"a"=>in_a, "b"=>in_b, "out"=>and_gate.out})
|
||||
dbg.out
|
||||
in_a.value=0
|
||||
in_b.value=0
|
||||
dbg.out
|
||||
in_a.value=1
|
||||
in_b.value=0
|
||||
dbg.out
|
||||
in_a.value=0
|
||||
in_b.value=1
|
||||
dbg.out
|
||||
in_a.value=1
|
||||
in_b.value=1
|
||||
dbg.out
|
||||
end
|
||||
end
|
||||
|
||||
class OrGate < Gate
|
||||
|
||||
def input_changed(new_value)
|
||||
orval = nil
|
||||
@inputs.each do |inport|
|
||||
if inport.is_defined?
|
||||
if orval == nil
|
||||
orval = inport.value
|
||||
else
|
||||
orval = orval | inport.value
|
||||
end
|
||||
else
|
||||
out.undefine
|
||||
return
|
||||
end
|
||||
end
|
||||
out.value = orval
|
||||
end
|
||||
|
||||
def self.test()
|
||||
puts "OR Test:"
|
||||
in_a = Port.new()
|
||||
in_b = Port.new()
|
||||
or_gate = OrGate.new(in_a, in_b)
|
||||
dbg = Dbg.new( {"a"=>in_a, "b"=>in_b, "out"=>or_gate.out})
|
||||
dbg.out
|
||||
in_a.value=0
|
||||
in_b.value=0
|
||||
dbg.out
|
||||
in_a.value=1
|
||||
in_b.value=0
|
||||
dbg.out
|
||||
in_a.value=0
|
||||
in_b.value=1
|
||||
dbg.out
|
||||
in_a.value=1
|
||||
in_b.value=1
|
||||
dbg.out
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class XorGate < Gate
|
||||
|
||||
def input_changed(new_value)
|
||||
xorval = nil
|
||||
@inputs.each do |inport|
|
||||
if inport.is_defined?
|
||||
if xorval == nil
|
||||
xorval = inport.value
|
||||
else
|
||||
xorval = xorval ^ inport.value
|
||||
end
|
||||
else
|
||||
out.undefine
|
||||
return
|
||||
end
|
||||
end
|
||||
out.value = xorval
|
||||
end
|
||||
|
||||
def self.test()
|
||||
puts "XOR Test:"
|
||||
in_a = Port.new()
|
||||
in_b = Port.new()
|
||||
or_gate = OrGate.new(in_a, in_b)
|
||||
dbg = Dbg.new( {"a"=>in_a, "b"=>in_b, "out"=>or_gate.out})
|
||||
dbg.out
|
||||
in_a.value=0
|
||||
in_b.value=0
|
||||
dbg.out
|
||||
in_a.value=1
|
||||
in_b.value=0
|
||||
dbg.out
|
||||
in_a.value=0
|
||||
in_b.value=1
|
||||
dbg.out
|
||||
in_a.value=1
|
||||
in_b.value=1
|
||||
dbg.out
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class Device
|
||||
def define_port(name, width=1, &callback)
|
||||
#create a method with the same name as the input
|
||||
#to assign a port to the input
|
||||
var_name = "@" + name
|
||||
port = Port.new(width)
|
||||
port.set_name(name)
|
||||
port.set_parent(self)
|
||||
instance_variable_set(var_name, port)
|
||||
define_singleton_method(name) do |other=nil|
|
||||
if other.class == NilClass #avoids overloaded compare for Port
|
||||
#getter
|
||||
instance_variable_get(var_name)
|
||||
else
|
||||
#connect to given port
|
||||
if other.class == Integer
|
||||
#convert constant
|
||||
other = PortConstant.new(width, other)
|
||||
end
|
||||
instance_variable_get(var_name).connect(other)
|
||||
self #for chained init calls
|
||||
end
|
||||
end
|
||||
if block_given?
|
||||
port.add_callback(&callback)
|
||||
end
|
||||
port
|
||||
end
|
||||
|
||||
def define_input(name, width=1)
|
||||
#automatically connects to on_change method
|
||||
define_port(name, width) { |new_value| on_change(new_value) }
|
||||
end
|
||||
|
||||
#for contained subdevices
|
||||
def define_device(name, device)
|
||||
var_name = "@" + name
|
||||
instance_variable_set(var_name, device)
|
||||
define_singleton_method(name) { instance_variable_get(var_name) }
|
||||
device.set_parent(self)
|
||||
device.set_name(name)
|
||||
end
|
||||
|
||||
#call on the hash of arguments at init
|
||||
def init_assign(hash)
|
||||
hash.each do |name, port|
|
||||
#check if there is a defined method (port) that matches
|
||||
if self.respond_to?(name)
|
||||
method(name).call(port)
|
||||
else
|
||||
raise ArgumentError, "No defined input '#{name}'"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def set_name(name)
|
||||
@name = name
|
||||
end
|
||||
|
||||
def set_parent(parent)
|
||||
@parent = parent
|
||||
end
|
||||
|
||||
def get_name
|
||||
if @name == nil
|
||||
@name = self.class
|
||||
end
|
||||
if @parent != nil
|
||||
@parent.get_name + "." + @name
|
||||
else
|
||||
@name
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
#logical shift right
|
||||
class LSR < Device
|
||||
def initialize(width, bitshift, init_args={})
|
||||
define_input("in", width)
|
||||
define_port("out", width)
|
||||
@bitshift = bitshift
|
||||
init_assign(init_args)
|
||||
end
|
||||
|
||||
def on_change(data_val)
|
||||
if @in.is_defined?
|
||||
out.value = @in.value >> @bitshift
|
||||
else
|
||||
out.undefine
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#logical shift left
|
||||
class LSL < Device
|
||||
def initialize(width, bitshift, init_args={})
|
||||
define_input("in", width)
|
||||
define_port("out", width)
|
||||
@bitshift = bitshift
|
||||
@outmask = (2**width) - 1
|
||||
init_assign(init_args)
|
||||
end
|
||||
|
||||
def on_change(data_val)
|
||||
if @in.is_defined?
|
||||
out.value = (@in.value << @bitshift) & @outmask
|
||||
else
|
||||
out.undefine
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class Reg < Device
|
||||
def initialize(width, init_args={})
|
||||
define_input("in", width)
|
||||
define_port("out", width)
|
||||
define_input("clk")
|
||||
define_input("en")
|
||||
define_input("rst")
|
||||
init_assign(init_args)
|
||||
end
|
||||
|
||||
def on_change(data_val)
|
||||
if rst.value == 1
|
||||
out.value = 0
|
||||
elsif en.value == 1 && clk.posedge?
|
||||
out.value = @in.value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Counter < Device
|
||||
def initialize(width, init_args={})
|
||||
define_input("in", width)
|
||||
define_port("out", width)
|
||||
define_input("clk")
|
||||
define_input("load")
|
||||
define_input("rst")
|
||||
init_assign(init_args)
|
||||
end
|
||||
|
||||
def on_change(data_val)
|
||||
#puts "on_change, clk=#{clk.value}, rst=#{rst.value}"
|
||||
if out.undefined?
|
||||
out.value=0
|
||||
end
|
||||
if rst.value == 1
|
||||
#puts "resetting"
|
||||
out.value = 0
|
||||
elsif clk.posedge?
|
||||
#puts "posedge"
|
||||
if load.value == 1
|
||||
out.value = @in.value
|
||||
else
|
||||
#puts "no load, old out=#{out.value}"
|
||||
out.value = out.value + 1
|
||||
#puts "new out=#{out.value}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.test
|
||||
puts "Reg Test:"
|
||||
din = Port.new(8)
|
||||
c = Port.new
|
||||
e = Port.new
|
||||
r = Port.new
|
||||
reg = Reg.new(8,{"in"=>din,"clk"=>c,"en"=>e,"rst"=>r})
|
||||
dbg = Dbg.new({"in"=>din, "clk"=>c, "en"=>e, "rst"=>r, "out"=>reg.out})
|
||||
r.value = 1
|
||||
c.value = 0
|
||||
e.value = 0
|
||||
din.value = 23
|
||||
dbg.out
|
||||
r.value = 1
|
||||
dbg.out
|
||||
r.value = 0
|
||||
dbg.out
|
||||
c.value = 1
|
||||
dbg.out
|
||||
e.value = 1
|
||||
dbg.out
|
||||
c.value = 0
|
||||
dbg.out
|
||||
c.value = 1
|
||||
dbg.out
|
||||
din.value = 37
|
||||
dbg.out
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class Mux < Device
|
||||
def initialize(data_width=1, select_width=1, init_args={})
|
||||
num_inputs = 2**select_width
|
||||
i=0
|
||||
@inputs = []
|
||||
num_inputs.times do
|
||||
@inputs << define_input("in"+i.to_s, data_width)
|
||||
i += 1
|
||||
end
|
||||
define_input("sel", select_width)
|
||||
define_port("out", data_width)
|
||||
init_assign(init_args)
|
||||
end
|
||||
|
||||
def on_change(new_value)
|
||||
if sel.is_defined?
|
||||
|
||||
out.value = @inputs[sel.value].value
|
||||
else
|
||||
out.undefine
|
||||
end
|
||||
end
|
||||
|
||||
def self.test
|
||||
puts "Mux test:"
|
||||
select = Port.new(1)
|
||||
a = Port.new(4)
|
||||
b = Port.new(4)
|
||||
mux = Mux.new(4, 1).sel(select).in0(a).in1(b)
|
||||
dbg = Dbg.new({"sel"=>select, "0"=>a, "1"=>b, "out"=>mux.out})
|
||||
a.value = 3
|
||||
b.value = 14
|
||||
dbg.out
|
||||
select.value = 0
|
||||
dbg.out
|
||||
select.value = 1
|
||||
dbg.out
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class Decoder < Device
|
||||
def initialize(width, init_args={})
|
||||
@width=width
|
||||
define_input("sel", width)
|
||||
define_input("en")
|
||||
num_of_outputs=2**width
|
||||
i=0
|
||||
@outputs = []
|
||||
num_of_outputs.times do
|
||||
#create an ordered list of all the outputs
|
||||
@outputs << define_port("o"+i.to_s)
|
||||
i+=1
|
||||
end
|
||||
init_assign(init_args)
|
||||
end
|
||||
|
||||
def on_change(data_val)
|
||||
if en
|
||||
if sel.is_defined?
|
||||
channel=sel.value
|
||||
i=0
|
||||
(@width**2).times do
|
||||
if i==channel
|
||||
@outputs[i].value=1
|
||||
else
|
||||
@outputs[i].value=0
|
||||
end
|
||||
i+=1
|
||||
end
|
||||
else
|
||||
i=0
|
||||
(@width**2).times do
|
||||
@outputs[i].undefine
|
||||
i+=1
|
||||
end
|
||||
end
|
||||
else
|
||||
i=0
|
||||
(@width**2).times do
|
||||
@outputs[i].value=0
|
||||
i+=1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.test
|
||||
puts "Decoder Test:"
|
||||
select=Port.new(2)
|
||||
decoder=Decoder.new(2,"sel"=>select)
|
||||
dbg = Dbg.new({"sel"=>select, "0"=>decoder.o0, "1"=>decoder.o1, "2"=>decoder.o2, "3"=>decoder.o3})
|
||||
dbg.out
|
||||
select.value=0
|
||||
dbg.out
|
||||
select.value=1
|
||||
dbg.out
|
||||
select.value=2
|
||||
dbg.out
|
||||
select.value=3
|
||||
dbg.out
|
||||
end
|
||||
end
|
||||
|
||||
class PEncoder < Device
|
||||
def initialize(width, init_args={})
|
||||
define_port("out", width)
|
||||
@inputs = []
|
||||
@num_inputs = width**2
|
||||
(0...@num_inputs).each do |i|
|
||||
@inputs << define_input("in#{i}", 1)
|
||||
end
|
||||
init_assign(init_args)
|
||||
end
|
||||
|
||||
def on_change(data_val)
|
||||
(0...@num_inputs).each do |i|
|
||||
if @inputs[i].is_defined?
|
||||
if @inputs[i].value == 1
|
||||
out.value = i
|
||||
return
|
||||
end
|
||||
else
|
||||
out.undefine
|
||||
return
|
||||
end
|
||||
end
|
||||
out.value = 0 #default, no active inputs
|
||||
end
|
||||
|
||||
def self.test
|
||||
puts "PEncoder test:"
|
||||
in0 = Port.new
|
||||
in1 = Port.new
|
||||
in2 = Port.new
|
||||
in3 = Port.new
|
||||
out = Port.new(2)
|
||||
dut = PEncoder.new(2).in0(in0)
|
||||
.in1(in1)
|
||||
.in2(in2)
|
||||
.in3(in3)
|
||||
.out(out)
|
||||
dbg = Dbg.new({"0"=>dut.in0, "1"=>dut.in1, "2"=>dut.in2, "3"=>dut.in3, "Out"=>dut.out})
|
||||
dbg.out
|
||||
in0.value = 0
|
||||
in1.value = 0
|
||||
in2.value = 0
|
||||
in3.value = 0
|
||||
dbg.out
|
||||
in2.value = 1
|
||||
dbg.out
|
||||
in1.value = 1
|
||||
dbg.out
|
||||
in3.value = 1
|
||||
dbg.out
|
||||
end
|
||||
end
|
||||
|
||||
class Adder < Device
|
||||
def initialize(width, init_args)
|
||||
define_input("a", width)
|
||||
define_input("b", width)
|
||||
define_port("out", width)
|
||||
init_assign(init_args)
|
||||
@mask = 2**width - 1
|
||||
end
|
||||
|
||||
def on_change(data_val)
|
||||
if a.is_defined? && b.is_defined?
|
||||
out.value = (a.value + b.value) & @mask
|
||||
else
|
||||
out.undefine
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Subtractor < Device
|
||||
def initialize(width, init_args)
|
||||
define_input("a", width)
|
||||
define_input("b", width)
|
||||
define_port("out", width)
|
||||
init_assign(init_args)
|
||||
@mask = 2**width - 1
|
||||
end
|
||||
|
||||
def on_change(data_val)
|
||||
if a.is_defined? && b.is_defined?
|
||||
out.value = (a.value - b.value) & @mask
|
||||
else
|
||||
out.undefine
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Ram < Device
|
||||
def initialize(data_width, addr_width, init_args={})
|
||||
@mem=[]
|
||||
define_input("in", data_width)
|
||||
define_port("out", data_width)
|
||||
define_input("addr", addr_width)
|
||||
define_input("clk")
|
||||
define_input("wr")
|
||||
init_assign(init_args)
|
||||
end
|
||||
|
||||
def set_data(init_array)
|
||||
@mem = init_array
|
||||
end
|
||||
|
||||
def on_change(data_val)
|
||||
if wr.value == 1 && clk.posedge?
|
||||
@mem[addr.value]=@in.value
|
||||
out.undefine
|
||||
elsif addr.is_defined? && @mem[addr.value] != nil
|
||||
out.value=@mem[addr.value]
|
||||
else
|
||||
out.value=0
|
||||
end
|
||||
end
|
||||
|
||||
def [](addr)
|
||||
return @mem[addr]
|
||||
end
|
||||
def []=(addr,value)
|
||||
if value.class==String
|
||||
@mem[addr]=value.to_i(2)
|
||||
else
|
||||
@mem[addr]=value
|
||||
end
|
||||
end
|
||||
def self.test
|
||||
puts "RAM test:"
|
||||
din=Port.new(8)
|
||||
addr=Port.new(8)
|
||||
wr=Port.new
|
||||
clk=Port.new
|
||||
ram=Ram.new(8,8,{"in"=>din,"addr"=>addr,"wr"=>wr,"clk"=>clk})
|
||||
dbg=Dbg.new({"in"=>din,"out"=>ram.out,"addr"=>addr,"wr"=>wr,"clk"=>clk})
|
||||
clk.value=0
|
||||
din.value=11
|
||||
addr.value=0
|
||||
wr.value=1
|
||||
clk.value=1
|
||||
dbg.out
|
||||
clk.value=0
|
||||
end
|
||||
end
|
||||
|
||||
class Dbg
|
||||
def initialize(ports,show_num=false)
|
||||
@names = []
|
||||
@ports = {}
|
||||
@show_num=show_num
|
||||
ports.each do |name,port|
|
||||
@names.push(name)
|
||||
@ports[name] = port
|
||||
end
|
||||
end
|
||||
|
||||
def add_trigger(port_name)
|
||||
@ports[port_name].add_late_callback { |value| self.out }
|
||||
end
|
||||
|
||||
def out
|
||||
watch_str = ""
|
||||
if @show_num
|
||||
@names.each { |name| watch_str += "#{name}=#{@ports[name].value} " }
|
||||
else
|
||||
@names.each { |name| watch_str += "#{name}=#{@ports[name].bitstring} " }
|
||||
end
|
||||
puts watch_str
|
||||
end
|
||||
end
|
||||
|
||||
def test()
|
||||
classes = []
|
||||
ObjectSpace.each_object { |o| classes.push o if o.class == Class }
|
||||
classes.each do |c|
|
||||
if c!=Object && c.methods.include?(:test)
|
||||
c.test
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# PEncoder.test
|
||||
test()
|
54
old/reg_fb_test.rb
Executable file
54
old/reg_fb_test.rb
Executable file
@ -0,0 +1,54 @@
|
||||
#testing propagation of nets by putting three
|
||||
#clocked registers in a loop
|
||||
|
||||
require_relative "rcircuit_lib"
|
||||
|
||||
clk = Port.new
|
||||
sel = Port.new
|
||||
rst = Port.new
|
||||
reg_en = Port.new
|
||||
init_val = Port.new(8)
|
||||
|
||||
reg_a = Reg.new(8).clk(clk).rst(rst).en(reg_en)
|
||||
reg_b = Reg.new(8).clk(clk).rst(rst).en(reg_en)
|
||||
reg_c = Reg.new(8).clk(clk).rst(rst).en(reg_en)
|
||||
#for loading reg_a with an initial value
|
||||
init_mux = Mux.new(8,1).in0(reg_c.out).in1(init_val).sel(sel)
|
||||
reg_a.in(init_mux.out)
|
||||
reg_b.in(reg_a.out)
|
||||
reg_c.in(reg_b.out)
|
||||
|
||||
dbg=Dbg.new("clk"=>clk,
|
||||
"init"=>init_val,
|
||||
"a.out"=>reg_a.out,
|
||||
"b.out"=>reg_b.out,
|
||||
"c.out"=>reg_c.out)
|
||||
|
||||
rst.value = 1
|
||||
clk.value = 0
|
||||
rst.value = 0
|
||||
reg_en.value = 1
|
||||
init_val.value = 1
|
||||
sel.value = 1
|
||||
puts "Loading 1 into A"
|
||||
clk.value = 1 #loads 1 into A
|
||||
dbg.out
|
||||
init_val.value = 2
|
||||
clk.value = 0
|
||||
puts "Loading 2 into A, 1 into B"
|
||||
clk.value = 1 #loads 2 into A, 1 into B
|
||||
dbg.out
|
||||
puts "4 clock clyles"
|
||||
sel.value = 0
|
||||
clk.value = 0
|
||||
clk.value = 1
|
||||
dbg.out
|
||||
clk.value = 0
|
||||
clk.value = 1
|
||||
dbg.out
|
||||
clk.value = 0
|
||||
clk.value = 1
|
||||
dbg.out
|
||||
clk.value = 0
|
||||
clk.value = 1
|
||||
dbg.out
|
45
old/reg_swap_test.rb
Executable file
45
old/reg_swap_test.rb
Executable file
@ -0,0 +1,45 @@
|
||||
#testing propagation of nets by using two registers wired to each other
|
||||
|
||||
require_relative "rcircuit_lib"
|
||||
|
||||
|
||||
clk = Port.new
|
||||
sel = Port.new
|
||||
rst = Port.new
|
||||
reg_en = Port.new
|
||||
init_val = Port.new(8)
|
||||
|
||||
reg_a = Reg.new(8).clk(clk).rst(rst).en(reg_en)
|
||||
reg_b = Reg.new(8).clk(clk).rst(rst).en(reg_en)
|
||||
#for loading reg_a with an initial value
|
||||
init_mux = Mux.new(8,1).in0(reg_b.out).in1(init_val).sel(sel)
|
||||
reg_a.in(init_mux.out)
|
||||
reg_b.in(reg_a.out)
|
||||
|
||||
dbg=Dbg.new("clk"=>clk,"init"=>init_val,"a.out"=>reg_a.out,"b.out"=>reg_b.out)
|
||||
|
||||
rst.value = 1
|
||||
clk.value = 0
|
||||
rst.value = 0
|
||||
reg_en.value = 1
|
||||
init_val.value = 1
|
||||
sel.value = 1
|
||||
puts "Loading 1 into A"
|
||||
clk.value = 1 #loads 1 into A
|
||||
dbg.out
|
||||
init_val.value = 2
|
||||
clk.value = 0
|
||||
puts "Loading 2 into A, 1 into B"
|
||||
clk.value = 1 #loads 2 into A, 1 into B
|
||||
dbg.out
|
||||
puts "Swapping A<->B"
|
||||
sel.value = 0 #set to swap A <-> B
|
||||
clk.value = 0
|
||||
clk.value = 1
|
||||
dbg.out
|
||||
clk.value = 0
|
||||
clk.value = 1
|
||||
dbg.out
|
||||
clk.value = 0
|
||||
clk.value = 1
|
||||
dbg.out
|
Loading…
Reference in New Issue
Block a user