From cc0661aebacc6f45842ff79b38c6ae3144369baf Mon Sep 17 00:00:00 2001 From: LAV3002 Date: Mon, 16 Feb 2026 10:52:48 +0300 Subject: [PATCH 1/3] [PLOD] Base entities and semantics --- .gitignore | 1 + lib/ADL/builder.rb | 37 +----- lib/{ADL => Common}/base.rb | 4 +- lib/{ADL => Common}/scope.rb | 38 +++++- lib/{ADL => Common}/value.rb | 2 +- lib/{ADL => Common}/var.rb | 6 +- lib/Devices/ns16550.rb | 86 +++++++++++++ lib/SDL/plod.rb | 225 ++++++++++++++++++++++++++++++++++ lib/Target/RISC-V/32I.rb | 6 +- lib/Target/RISC-V/encoding.rb | 6 +- lib/d_ir_gen.rb | 7 ++ lib/ir_gen.rb | 6 +- 12 files changed, 373 insertions(+), 51 deletions(-) rename lib/{ADL => Common}/base.rb (96%) rename lib/{ADL => Common}/scope.rb (85%) rename lib/{ADL => Common}/value.rb (98%) rename lib/{ADL => Common}/var.rb (97%) create mode 100644 lib/Devices/ns16550.rb create mode 100644 lib/SDL/plod.rb create mode 100644 lib/d_ir_gen.rb diff --git a/.gitignore b/.gitignore index e3200e0..3d89848 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ /test/tmp/ /test/version_tmp/ /tmp/ +/sim_lib/generated # Used by dotenv library to load environment variables. # .env diff --git a/lib/ADL/builder.rb b/lib/ADL/builder.rb index 83d0313..18b33d2 100644 --- a/lib/ADL/builder.rb +++ b/lib/ADL/builder.rb @@ -1,35 +1,8 @@ -require_relative "scope" +require_relative "../Common/scope" require "Utility/type" -module SimInfra - class IrStmt - attr_reader :name, :oprnds, :attrs - def initialize(name, oprnds, attrs) - @name = name; @oprnds = oprnds; @attrs = attrs; - end - - def to_h - { - name: @name, - oprnds: @oprnds.map { |o| - if o.class == Var || o.class == Constant - o.to_h - else - o - end - }, - attrs: @attrs, - } - end - - def self.from_h(h) - IrStmt.new(h[:name], h[:oprnds], h[:attrs]) - end - end -end - # Basics -module SimInfra +module LangInfra def assert(condition, msg = nil); raise msg if !condition; end @@instructions = [] @@ -75,7 +48,7 @@ def self.from_h(h) end class InstructionInfoBuilder - include SimInfra + include LangInfra def initialize(name, feature) @info = InstructionInfo.new(name, feature) @@ -127,7 +100,7 @@ def Instruction(name, &block) end class InterfaceBuilder - include SimInfra + include LangInfra def function(name, output_types = [], input_types = []) @@interface_functions << {:name => name, :return_types => output_types, :argument_types => input_types} @@ -201,7 +174,7 @@ def RegFiles() end # * generate precise fields -module SimInfra +module LangInfra class RegisterFileBuilder def r32(sym, *args) @info.regs << Register.new(sym, 32, args[0] ? [args[0]] : []) diff --git a/lib/ADL/base.rb b/lib/Common/base.rb similarity index 96% rename from lib/ADL/base.rb rename to lib/Common/base.rb index ed8ae30..a2c50c6 100644 --- a/lib/ADL/base.rb +++ b/lib/Common/base.rb @@ -1,7 +1,7 @@ require_relative "value" # Testing infra -module SimInfra +module LangInfra # @@instructions -array of instruction description # shows result of our tests in interactive Ruby (IRB) or standalone def self.serialize(msg= nil) @@ -44,7 +44,7 @@ def self.state end # reset state - def siminfra_reset_module_state; @@instructions = []; end + def LangInfra_reset_module_state; @@instructions = []; end # mixin for global counter, function returns 0,1,2,.... module GlobalCounter diff --git a/lib/ADL/scope.rb b/lib/Common/scope.rb similarity index 85% rename from lib/ADL/scope.rb rename to lib/Common/scope.rb index 57d4643..da88453 100644 --- a/lib/ADL/scope.rb +++ b/lib/Common/scope.rb @@ -2,14 +2,41 @@ require_relative 'var' require 'Utility/type' -module SimInfra +module LangInfra + class IrStmt + attr_reader :name, :oprnds, :attrs + def initialize(name, oprnds, attrs) + @name = name; @oprnds = oprnds; @attrs = attrs; + end + + def to_h + { + name: @name, + oprnds: @oprnds.map { |o| + if o.class == Var || o.class == Constant + o.to_h + else + o + end + }, + attrs: @attrs, + } + end + + def self.from_h(h) + IrStmt.new(h[:name], h[:oprnds], h[:attrs]) + end + end +end + +module LangInfra def assert(condition, msg = nil) raise msg unless condition end class Scope include GlobalCounter # used for temp variables IDs - include SimInfra + include LangInfra attr_reader :tree, :vars, :parent, :mem @@ -27,12 +54,13 @@ def var(name, type, attrs = nil) end def method(name, type, regset = nil) - @vars[name] = SimInfra::Var.new(self, name, type, regset) # return var + @vars[name] = LangInfra::Var.new(self, name, type, regset) # return var instance_eval "def #{name}(); return @vars[:#{name}]; end", __FILE__, __LINE__ + @vars[name] end def rmethod(name, regset, type) - @vars[name] = SimInfra::Var.new(self, name, type, regset) # return var + @vars[name] = LangInfra::Var.new(self, name, type, regset) # return var instance_eval "def #{name}(); return @vars[:#{name}]; end", __FILE__, __LINE__ end @@ -113,6 +141,8 @@ def read(rfile, reg) def cast(expr, type) = stmt(:cast, [tmpvar(type), expr]) + def get_field(expr, lsb, type) = stmt(:get_field, [tmpvar(type), expr, lsb]) + def let(*args) case args.length when 3 diff --git a/lib/ADL/value.rb b/lib/Common/value.rb similarity index 98% rename from lib/ADL/value.rb rename to lib/Common/value.rb index 73db308..7a4535c 100644 --- a/lib/ADL/value.rb +++ b/lib/Common/value.rb @@ -1,4 +1,4 @@ -module SimInfra +module LangInfra # Value class is a super class of Variable or Constant. class Value # If value is nil then Value represents a variable. diff --git a/lib/ADL/var.rb b/lib/Common/var.rb similarity index 97% rename from lib/ADL/var.rb rename to lib/Common/var.rb index 671f258..cdc7fd3 100644 --- a/lib/ADL/var.rb +++ b/lib/Common/var.rb @@ -1,9 +1,9 @@ require_relative 'base' require 'Utility/type' -module SimInfra +module LangInfra class Var - include SimInfra + include LangInfra attr_reader :scope, :name, :type, :regset def initialize(scope, name, type, regset = nil) @@ -37,7 +37,7 @@ def self.from_h(h, scope) end end -module SimInfra +module LangInfra # class Var def +(other) = @scope.add(self, other) diff --git a/lib/Devices/ns16550.rb b/lib/Devices/ns16550.rb new file mode 100644 index 0000000..b1d8f79 --- /dev/null +++ b/lib/Devices/ns16550.rb @@ -0,0 +1,86 @@ +require_relative "../SDL/plod" + +module Devices + + Device(:ns16550) { + + Register(:rbr) { + size 0x1 + offset 0x0 + type :ro + enableIf { + let :tmp, :b1, lsr.dlab + } + } + + Register(:thr) { + size 0x1 + offset 0x0 + type :wo + # enableIf lsr.DLAB == 0 + } + + Register(:ier) { + size 0x1 + offset 0x1 + # enableIf lsr.DLAB == 0 + } + + Register(:iir) { + size 0x1 + offset 0x2 + type :ro + } + + Register(:fcr) { + size 0x1 + offset 0x2 + type :wo + } + + Register(:lcr) { + size 0x1 + offset 0x3 + } + + Register(:mcr) { + size 0x1 + offset 0x4 + } + + Register(:lsr) { + size 0x1 + offset 0x5 + field :dlab, 0x7 + } + + Register(:msr) { + size 0x1 + offset 0x6 + } + + Register(:scr) { + size 0x1 + offset 0x7 + } + + Register(:dll) { + size 0x1 + offset 0x0 + # enableIf lsr.DLAB == 1 + } + + Register(:dlm) { + size 0x1 + offset 0x1 + # enableIf lsr.DLAB == 1 + } + + # Function(:lol, int(:lol), bv(64, :dom), void) { + + # } + + + } + +end diff --git a/lib/SDL/plod.rb b/lib/SDL/plod.rb new file mode 100644 index 0000000..e3199ca --- /dev/null +++ b/lib/SDL/plod.rb @@ -0,0 +1,225 @@ +require_relative '../Common/scope' + +module Devices + + # class Oper + # attr_accessor :kind, :ret, :opds + + # def initialize(kind, ret, *opds) + # @kind = kind + # @ret = ret + # @opds = opds + # end + # end + + # class ConstExpr + # attr_accessor :type, :kind, :value + + # def initialize(type, value) + # @type = type + # @kind = :const + # @value = value + # end + # end + + # class VarExpr + # attr_accessor :type, :kind, :name + + # def initialize(type, name) + # @type = type + # @kind = :var + # @value = name + # end + # end + + # class ExprWrapper + # attr_accessor :expr + + # class << self + # attr_accessor :currentScope + # end + + # @@tmpCounter = 0 + # @@currentScope = nil + + # def initialize(expr) + # @expr = expr + # end + + # def self.setScope(scope) + # @@currentScope = scope + # end + + # def self.nthOp(n, kind, retType, *args) + # var = VarExpr(retType, "tmp_#{@@tmpCounter}".to_sym) + # @@tmpCounter += 1 + + # @@currentScope < Oper(kind, var, *(args.map { |arg| arg.expr })) + + # return ExprWrapper.new(var) + # end + + # def self.binOp(lhs, rhs, kind, retType = nil) + # if lhs.type != rhs.type + # raise "Bad bin op args" + # end + + # if retType.nil? + # retType = lhs.type + # end + + # return nthOp(2, kind, retType, lhs, rhs) + # end + + # def+(other); binOp(self, other, :add); end + # def-(other); binOp(self, other, :sub); end + # def<<(other); binOp(self, other, :shl); end + # def<(other); binOp(self, other, :lt, bv(1)); end + # def>(other); binOp(self, other, :gt, bv(1)); end + # def^(other); binOp(self, other, :xor); end + # def>>(other); binOp(self, other, :shr); end + # def|(other); binOp(self, other, :or) end + # def&(other); binOp(self, other, :and) end + # def==(other); binOp(self, other, :eq, bv(1)); end + # def!=(other); binOp(self, other, :ne, bv(1)); end + # # def[](r, l); @scope.extract(self, r, l); end + + # end + + # class Statement < Utils::BaseInfo(:retVar, :op, :args) + # def initialize() + # super(nil, nil, []) + # end + # end + + # class CallHandler + # attr_accessor :symbolName, :args, :next + + # def initialize(name, *args) + # @symbolName = name + # @args = args + # @next = nil + + # stmt = Statement.new() + # obj = getTmp() + # stmt.retVar = obj + # stmt.op = :getObj + # stmt.args = [callHandler.symbolName.to_sym] + # @@scopeInstance << stmt + + # if !callHandler.args.empty? + # callStmt = Statement.new() + # stmt.retVar = getTmp() + # stmt.op = :call + # stmt.args = [obj] + callHandler.args + # @@scopeInstance << callStmt + # end + + # puts @symbolName + # puts @args + # end + + # def method_missing(name, *args) + # @next = CallHandler.new(name, *args) + + # return @next + # end + # end + + # module Scope + # def self.method_missing(name, *args) + # return ExprWrapper.new(name, *args) + # end + # end + + @@desc = { + devices: [] + } + + @@semablocks = [] + + def self.getSemablocks + @@semablocks + end + + def self.initScope + scope = LangInfra::Scope.new(nil) + @@desc[:devices][0][:registers].each do |reg| + regvar = scope.method(reg[:name], :regref) + + reg[:fields].each do |field| + regvar.define_singleton_method(field[:name]) do + @scope.get_field(self, field[:lsb], ('b' + field[:size].to_s).to_sym) + end + end + end + + return scope + end + + def self.processSemablocks + @@semablocks.each do |block| + scope = initScope + scope.instance_eval(&block[2]) + block[0][block[1]] = scope.to_h + # puts block[0] + end + + @@semablocks = [] + end + + def self.getDesc + @@desc + end + + class RegBuilder + attr_accessor :info + + def initialize(name) + @info = { name: name, fields: [] } + end + + def size(value) + @info[:size] = value + end + + def offset(value) + @info[:offset] = value + end + + def type(value) + @info[:type] = value + end + + def enableIf(&block) + Devices.getSemablocks << [@info, :enableIf, block] + end + + def field(name, lsb, size = 1) + @info[:fields] << { name: name, lsb: lsb, size: size } + end + end + + class DeviceBuilder + attr_accessor :info + + def initialize(name) + @info = { name: name, registers: [] } + end + + def Register(name, &block) + regBuilder = RegBuilder.new(name) + regBuilder.instance_eval(&block) + @info[:registers] << regBuilder.info + nil + end + end + + def self.Device(name, &block) + deviceBuilder = DeviceBuilder.new(name) + deviceBuilder.instance_eval(&block) + @@desc[:devices] << deviceBuilder.info + Devices.processSemablocks + nil + end +end diff --git a/lib/Target/RISC-V/32I.rb b/lib/Target/RISC-V/32I.rb index 6a67a7b..f18988e 100644 --- a/lib/Target/RISC-V/32I.rb +++ b/lib/Target/RISC-V/32I.rb @@ -1,10 +1,10 @@ require_relative "encoding" -require_relative "../../ADL/base" +require_relative "../../Common/base" require_relative "../../ADL/builder" module RV32I - include SimInfra - extend SimInfra + include LangInfra + extend LangInfra Interface { function :sysCall diff --git a/lib/Target/RISC-V/encoding.rb b/lib/Target/RISC-V/encoding.rb index 5ff0310..c1928c1 100644 --- a/lib/Target/RISC-V/encoding.rb +++ b/lib/Target/RISC-V/encoding.rb @@ -1,6 +1,6 @@ -require_relative "../../ADL/base" +require_relative "../../Common/base" -module SimInfra +module LangInfra def u_imm(imm) return imm, :s32, "let :#{imm}, [:op], :s32, f_#{imm}.s << 12" end @@ -30,7 +30,7 @@ def xreg(name) end end -module SimInfra +module LangInfra def format_u(opcode) return :U, [ field(:f_opcode, 6, 0, opcode), diff --git a/lib/d_ir_gen.rb b/lib/d_ir_gen.rb new file mode 100644 index 0000000..d56ea4a --- /dev/null +++ b/lib/d_ir_gen.rb @@ -0,0 +1,7 @@ +#!/usr/bin/ruby +# frozen_string_literal: true +require 'yaml' + +require 'Devices/ns16550' + +puts Devices.getDesc.to_yaml diff --git a/lib/ir_gen.rb b/lib/ir_gen.rb index 7450323..d853da1 100644 --- a/lib/ir_gen.rb +++ b/lib/ir_gen.rb @@ -1,16 +1,16 @@ #!/usr/bin/ruby # frozen_string_literal: true -require 'ADL/base' +require 'Common/base' require 'ADL/builder' require 'Target/RISC-V/32I' require 'yaml' -SimInfra.serialize +LangInfra.serialize yaml_data = YAML.safe_load( - SimInfra.state, + LangInfra.state, permitted_classes: [Symbol], symbolize_names: true ) From c27e1bfd61192e155ecf96a28b46bbb6a70a7cb3 Mon Sep 17 00:00:00 2001 From: LAV3002 Date: Tue, 17 Feb 2026 10:11:05 +0300 Subject: [PATCH 2/3] Review fixes --- .gitignore | 2 + lib/ADL/builder.rb | 10 +-- lib/Common/base.rb | 4 +- lib/Common/scope.rb | 10 +-- lib/Common/value.rb | 2 +- lib/Common/var.rb | 6 +- lib/Devices/ns16550.rb | 11 +-- lib/SDL/plod.rb | 136 +--------------------------------- lib/Target/RISC-V/32I.rb | 8 +- lib/Target/RISC-V/32ILoops.rb | 4 +- lib/Target/RISC-V/encoding.rb | 6 +- lib/ir_gen.rb | 4 +- 12 files changed, 32 insertions(+), 171 deletions(-) diff --git a/.gitignore b/.gitignore index 3d89848..5af20d7 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,8 @@ /test/version_tmp/ /tmp/ /sim_lib/generated +IR.json +IR.yaml # Used by dotenv library to load environment variables. # .env diff --git a/lib/ADL/builder.rb b/lib/ADL/builder.rb index 18b33d2..d1f62c6 100644 --- a/lib/ADL/builder.rb +++ b/lib/ADL/builder.rb @@ -1,8 +1,8 @@ -require_relative "../Common/scope" +require "Common/scope" require "Utility/type" # Basics -module LangInfra +module Protea def assert(condition, msg = nil); raise msg if !condition; end @@instructions = [] @@ -48,7 +48,7 @@ def self.from_h(h) end class InstructionInfoBuilder - include LangInfra + include Protea def initialize(name, feature) @info = InstructionInfo.new(name, feature) @@ -100,7 +100,7 @@ def Instruction(name, &block) end class InterfaceBuilder - include LangInfra + include Protea def function(name, output_types = [], input_types = []) @@interface_functions << {:name => name, :return_types => output_types, :argument_types => input_types} @@ -174,7 +174,7 @@ def RegFiles() end # * generate precise fields -module LangInfra +module Protea class RegisterFileBuilder def r32(sym, *args) @info.regs << Register.new(sym, 32, args[0] ? [args[0]] : []) diff --git a/lib/Common/base.rb b/lib/Common/base.rb index a2c50c6..bfd046d 100644 --- a/lib/Common/base.rb +++ b/lib/Common/base.rb @@ -1,7 +1,7 @@ require_relative "value" # Testing infra -module LangInfra +module Protea # @@instructions -array of instruction description # shows result of our tests in interactive Ruby (IRB) or standalone def self.serialize(msg= nil) @@ -44,7 +44,7 @@ def self.state end # reset state - def LangInfra_reset_module_state; @@instructions = []; end + def Protea_reset_module_state; @@instructions = []; end # mixin for global counter, function returns 0,1,2,.... module GlobalCounter diff --git a/lib/Common/scope.rb b/lib/Common/scope.rb index da88453..57fb19c 100644 --- a/lib/Common/scope.rb +++ b/lib/Common/scope.rb @@ -2,7 +2,7 @@ require_relative 'var' require 'Utility/type' -module LangInfra +module Protea class IrStmt attr_reader :name, :oprnds, :attrs def initialize(name, oprnds, attrs) @@ -29,14 +29,14 @@ def self.from_h(h) end end -module LangInfra +module Protea def assert(condition, msg = nil) raise msg unless condition end class Scope include GlobalCounter # used for temp variables IDs - include LangInfra + include Protea attr_reader :tree, :vars, :parent, :mem @@ -54,13 +54,13 @@ def var(name, type, attrs = nil) end def method(name, type, regset = nil) - @vars[name] = LangInfra::Var.new(self, name, type, regset) # return var + @vars[name] = Protea::Var.new(self, name, type, regset) # return var instance_eval "def #{name}(); return @vars[:#{name}]; end", __FILE__, __LINE__ @vars[name] end def rmethod(name, regset, type) - @vars[name] = LangInfra::Var.new(self, name, type, regset) # return var + @vars[name] = Protea::Var.new(self, name, type, regset) # return var instance_eval "def #{name}(); return @vars[:#{name}]; end", __FILE__, __LINE__ end diff --git a/lib/Common/value.rb b/lib/Common/value.rb index 7a4535c..66d4b34 100644 --- a/lib/Common/value.rb +++ b/lib/Common/value.rb @@ -1,4 +1,4 @@ -module LangInfra +module Protea # Value class is a super class of Variable or Constant. class Value # If value is nil then Value represents a variable. diff --git a/lib/Common/var.rb b/lib/Common/var.rb index cdc7fd3..730de03 100644 --- a/lib/Common/var.rb +++ b/lib/Common/var.rb @@ -1,9 +1,9 @@ require_relative 'base' require 'Utility/type' -module LangInfra +module Protea class Var - include LangInfra + include Protea attr_reader :scope, :name, :type, :regset def initialize(scope, name, type, regset = nil) @@ -37,7 +37,7 @@ def self.from_h(h, scope) end end -module LangInfra +module Protea # class Var def +(other) = @scope.add(self, other) diff --git a/lib/Devices/ns16550.rb b/lib/Devices/ns16550.rb index b1d8f79..7c7f9f2 100644 --- a/lib/Devices/ns16550.rb +++ b/lib/Devices/ns16550.rb @@ -9,7 +9,7 @@ module Devices offset 0x0 type :ro enableIf { - let :tmp, :b1, lsr.dlab + lsr.dlab } } @@ -17,13 +17,11 @@ module Devices size 0x1 offset 0x0 type :wo - # enableIf lsr.DLAB == 0 } Register(:ier) { size 0x1 offset 0x1 - # enableIf lsr.DLAB == 0 } Register(:iir) { @@ -67,20 +65,13 @@ module Devices Register(:dll) { size 0x1 offset 0x0 - # enableIf lsr.DLAB == 1 } Register(:dlm) { size 0x1 offset 0x1 - # enableIf lsr.DLAB == 1 } - # Function(:lol, int(:lol), bv(64, :dom), void) { - - # } - - } end diff --git a/lib/SDL/plod.rb b/lib/SDL/plod.rb index e3199ca..65e890c 100644 --- a/lib/SDL/plod.rb +++ b/lib/SDL/plod.rb @@ -1,137 +1,6 @@ -require_relative '../Common/scope' +require 'Common/scope' module Devices - - # class Oper - # attr_accessor :kind, :ret, :opds - - # def initialize(kind, ret, *opds) - # @kind = kind - # @ret = ret - # @opds = opds - # end - # end - - # class ConstExpr - # attr_accessor :type, :kind, :value - - # def initialize(type, value) - # @type = type - # @kind = :const - # @value = value - # end - # end - - # class VarExpr - # attr_accessor :type, :kind, :name - - # def initialize(type, name) - # @type = type - # @kind = :var - # @value = name - # end - # end - - # class ExprWrapper - # attr_accessor :expr - - # class << self - # attr_accessor :currentScope - # end - - # @@tmpCounter = 0 - # @@currentScope = nil - - # def initialize(expr) - # @expr = expr - # end - - # def self.setScope(scope) - # @@currentScope = scope - # end - - # def self.nthOp(n, kind, retType, *args) - # var = VarExpr(retType, "tmp_#{@@tmpCounter}".to_sym) - # @@tmpCounter += 1 - - # @@currentScope < Oper(kind, var, *(args.map { |arg| arg.expr })) - - # return ExprWrapper.new(var) - # end - - # def self.binOp(lhs, rhs, kind, retType = nil) - # if lhs.type != rhs.type - # raise "Bad bin op args" - # end - - # if retType.nil? - # retType = lhs.type - # end - - # return nthOp(2, kind, retType, lhs, rhs) - # end - - # def+(other); binOp(self, other, :add); end - # def-(other); binOp(self, other, :sub); end - # def<<(other); binOp(self, other, :shl); end - # def<(other); binOp(self, other, :lt, bv(1)); end - # def>(other); binOp(self, other, :gt, bv(1)); end - # def^(other); binOp(self, other, :xor); end - # def>>(other); binOp(self, other, :shr); end - # def|(other); binOp(self, other, :or) end - # def&(other); binOp(self, other, :and) end - # def==(other); binOp(self, other, :eq, bv(1)); end - # def!=(other); binOp(self, other, :ne, bv(1)); end - # # def[](r, l); @scope.extract(self, r, l); end - - # end - - # class Statement < Utils::BaseInfo(:retVar, :op, :args) - # def initialize() - # super(nil, nil, []) - # end - # end - - # class CallHandler - # attr_accessor :symbolName, :args, :next - - # def initialize(name, *args) - # @symbolName = name - # @args = args - # @next = nil - - # stmt = Statement.new() - # obj = getTmp() - # stmt.retVar = obj - # stmt.op = :getObj - # stmt.args = [callHandler.symbolName.to_sym] - # @@scopeInstance << stmt - - # if !callHandler.args.empty? - # callStmt = Statement.new() - # stmt.retVar = getTmp() - # stmt.op = :call - # stmt.args = [obj] + callHandler.args - # @@scopeInstance << callStmt - # end - - # puts @symbolName - # puts @args - # end - - # def method_missing(name, *args) - # @next = CallHandler.new(name, *args) - - # return @next - # end - # end - - # module Scope - # def self.method_missing(name, *args) - # return ExprWrapper.new(name, *args) - # end - # end - @@desc = { devices: [] } @@ -143,7 +12,7 @@ def self.getSemablocks end def self.initScope - scope = LangInfra::Scope.new(nil) + scope = Protea::Scope.new(nil) @@desc[:devices][0][:registers].each do |reg| regvar = scope.method(reg[:name], :regref) @@ -162,7 +31,6 @@ def self.processSemablocks scope = initScope scope.instance_eval(&block[2]) block[0][block[1]] = scope.to_h - # puts block[0] end @@semablocks = [] diff --git a/lib/Target/RISC-V/32I.rb b/lib/Target/RISC-V/32I.rb index f18988e..87648df 100644 --- a/lib/Target/RISC-V/32I.rb +++ b/lib/Target/RISC-V/32I.rb @@ -1,10 +1,10 @@ require_relative "encoding" -require_relative "../../Common/base" -require_relative "../../ADL/builder" +require "Common/base" +require "ADL/builder" module RV32I - include LangInfra - extend LangInfra + include Protea + extend Protea Interface { function :sysCall diff --git a/lib/Target/RISC-V/32ILoops.rb b/lib/Target/RISC-V/32ILoops.rb index 07b0b06..3b3c6c5 100644 --- a/lib/Target/RISC-V/32ILoops.rb +++ b/lib/Target/RISC-V/32ILoops.rb @@ -1,6 +1,6 @@ require_relative "encoding" -require_relative "../../Generic/base" -require_relative "../../Generic/builder" +require "Generic/base" +require "Generic/builder" module Ops diff --git a/lib/Target/RISC-V/encoding.rb b/lib/Target/RISC-V/encoding.rb index c1928c1..3c0ed79 100644 --- a/lib/Target/RISC-V/encoding.rb +++ b/lib/Target/RISC-V/encoding.rb @@ -1,6 +1,6 @@ -require_relative "../../Common/base" +require "Common/base" -module LangInfra +module Protea def u_imm(imm) return imm, :s32, "let :#{imm}, [:op], :s32, f_#{imm}.s << 12" end @@ -30,7 +30,7 @@ def xreg(name) end end -module LangInfra +module Protea def format_u(opcode) return :U, [ field(:f_opcode, 6, 0, opcode), diff --git a/lib/ir_gen.rb b/lib/ir_gen.rb index d853da1..2b71e4c 100644 --- a/lib/ir_gen.rb +++ b/lib/ir_gen.rb @@ -7,10 +7,10 @@ require 'yaml' -LangInfra.serialize +Protea.serialize yaml_data = YAML.safe_load( - LangInfra.state, + Protea.state, permitted_classes: [Symbol], symbolize_names: true ) From a2617ac6188840d022731e8749fe9215c0f741b9 Mon Sep 17 00:00:00 2001 From: LAV3002 Date: Thu, 5 Mar 2026 15:03:10 +0300 Subject: [PATCH 3/3] Type and Structs --- .gitignore | 1 + lib/Common/scope.rb | 26 +++-- lib/Common/type.rb | 71 +++++++++++++ lib/Common/var.rb | 28 ++--- lib/Devices/fifo8.c | 190 ++++++++++++++++++++++++++++++++++ lib/Devices/fifo8.h | 230 +++++++++++++++++++++++++++++++++++++++++ lib/Devices/ns16550.rb | 154 +++++++++++++++------------ lib/Devices/serial.h | 88 ++++++++++++++++ lib/SDL/plod.rb | 147 +++++++++++++++++++------- lib/d_ir_gen.rb | 4 +- 10 files changed, 819 insertions(+), 120 deletions(-) create mode 100644 lib/Common/type.rb create mode 100644 lib/Devices/fifo8.c create mode 100644 lib/Devices/fifo8.h create mode 100644 lib/Devices/serial.h diff --git a/.gitignore b/.gitignore index 5af20d7..ba92271 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ /sim_lib/generated IR.json IR.yaml +/lib/Devices/serial.c # Used by dotenv library to load environment variables. # .env diff --git a/lib/Common/scope.rb b/lib/Common/scope.rb index 57fb19c..0b855ea 100644 --- a/lib/Common/scope.rb +++ b/lib/Common/scope.rb @@ -48,13 +48,13 @@ def initialize(parent) end # resolve allows to convert Ruby Integer constants to Constant instance - def var(name, type, attrs = nil) - method(name, type) + def var(name, type, attrs = nil, plod_type = Type::Empty.new()) + method(name, type, nil, plod_type) stmt :new_var, [@vars[name]], attrs # returns @vars[name] end - def method(name, type, regset = nil) - @vars[name] = Protea::Var.new(self, name, type, regset) # return var + def method(name, type, regset = nil, plod_type = Type::Empty.new()) + @vars[name] = Protea::Var.new(self, name, type, regset, plod_type) # return var instance_eval "def #{name}(); return @vars[:#{name}]; end", __FILE__, __LINE__ @vars[name] end @@ -141,8 +141,6 @@ def read(rfile, reg) def cast(expr, type) = stmt(:cast, [tmpvar(type), expr]) - def get_field(expr, lsb, type) = stmt(:get_field, [tmpvar(type), expr, lsb]) - def let(*args) case args.length when 3 @@ -182,7 +180,7 @@ def arlet(sym, regset, attrs, type, expr) def branch(expr) = stmt(:branch, [expr]) - private def tmpvar(type) = var("_tmp#{next_counter}".to_sym, type) + private def tmpvar(type, plod_type = Type::Empty.new()) = var("_tmp#{next_counter}".to_sym, type, nil, plod_type) # stmtadds statement into tree and retursoperand[0] # which result in near all cases def stmt(name, operands, attrs = nil) @@ -203,6 +201,20 @@ def read_transform(operation_name, op) end end + # PLOD specific part + + def get_field(expr, lsb, type) = stmt(:get_field, [tmpvar(type), expr, lsb]) + + + def get_container_element(plod_type, container, idx) = stmt(:get, [tmpvar(plod_type.name, plod_type), container, idx]) + + def insert_var(name, var) + var.scope = self + @vars[name] = var + instance_eval "def #{name}(); return @vars[:#{name}]; end", __FILE__, __LINE__ + var + end + def to_h { tree: @tree.map(&:to_h) diff --git a/lib/Common/type.rb b/lib/Common/type.rb new file mode 100644 index 0000000..36d7f97 --- /dev/null +++ b/lib/Common/type.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true + +# Main module of Protea +module Protea + # Value class is a super class of Variable or Constant. + module Type + # Base module for every type + module TypeObject end + + # TODO + module EmptyInterface end + + def self.TypeObject(type_name, *fields_with_inter) + interface = EmptyInterface + + if fields_with_inter.last.is_a?(Module) + interface = fields_with_inter.last + fields_with_inter.pop + end + + Class.new(Struct.new(*fields_with_inter)) do + include TypeObject + + define_method(:name) do + field_names = {} + + to_h.each do |key, value| + field_names[key] = value.to_s + end + + type_name = type_name.to_s + (type_name % field_names).to_sym + end + + define_method(:to_s) do + name + end + + define_method(:inject) do |var| + var.extend(interface) + end + end + end + + # Empty type for ADL var + Empty = TypeObject(:empty) + + # Just Int + Int = TypeObject(:int) + + # TODO + module ArrayInter + def get(index) + @scope.get_container_element(plod_type.stored_type, self, index) + end + + def set(index, value) + @scope.stmt(:set, [self, index, value]) + end + + def +(value) + @scope.stmt(:let, [self, value]) + end + end + + Array = TypeObject('%{stored_type}[%{size}]', :stored_type, :size, ArrayInter) + + # Analogue of array + Bitvector = TypeObject('b%{size}', :size) + end +end diff --git a/lib/Common/var.rb b/lib/Common/var.rb index 730de03..a39202b 100644 --- a/lib/Common/var.rb +++ b/lib/Common/var.rb @@ -1,16 +1,20 @@ require_relative 'base' +require_relative 'type' require 'Utility/type' module Protea class Var include Protea - attr_reader :scope, :name, :type, :regset + attr_reader :name, :regset, :plod_type + attr_accessor :scope, :type - def initialize(scope, name, type, regset = nil) + def initialize(scope, name, type, regset = nil, plod_type = Type::Empty.new()) @scope = scope @name = name @type = type @regset = regset + @plod_type = plod_type + @plod_type.inject(self) end # Syntax "var[]=value" is used to assign variable @@ -60,16 +64,16 @@ def s = @scope.cast(self, ('s' + Utility.get_type(@type).bitsize.to_s).to_sym) def b = @scope.cast(self, ('b' + Utility.get_type(@type).bitsize.to_s).to_sym) def r(regset) = @scope.get_reg(self, regset, ('r' + Utility.get_type(@type).bitsize.to_s).to_sym) - def method_missing(name, *regset) - if regset.empty? - instance_eval "def #{name}(); @scope.cast(self, (#{name}).to_sym); end", __FILE__, __LINE__ - @scope.cast(self, name.to_sym) - else - instance_eval "def #{name}(regset); @scope.get_reg(self, regset, (#{name}).to_sym); end", __FILE__, - __LINE__ - 1 - @scope.get_reg(self, *regset, name.to_sym) - end - end + # def method_missing(name, *regset) + # if regset.empty? + # instance_eval "def #{name}(); @scope.cast(self, (#{name}).to_sym); end", __FILE__, __LINE__ + # @scope.cast(self, name.to_sym) + # else + # instance_eval "def #{name}(regset); @scope.get_reg(self, regset, (#{name}).to_sym); end", __FILE__, + # __LINE__ - 1 + # @scope.get_reg(self, *regset, name.to_sym) + # end + # end def set_regset(regset) @regset = regset diff --git a/lib/Devices/fifo8.c b/lib/Devices/fifo8.c new file mode 100644 index 0000000..a26da66 --- /dev/null +++ b/lib/Devices/fifo8.c @@ -0,0 +1,190 @@ +/* + * Generic FIFO component, implemented as a circular buffer. + * + * Copyright (c) 2012 Peter A. G. Crosthwaite + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include "qemu/osdep.h" +#include "migration/vmstate.h" +#include "qemu/fifo8.h" + +void fifo8_reset(Fifo8 *fifo) +{ + fifo->num = 0; + fifo->head = 0; +} + +void fifo8_create(Fifo8 *fifo, uint32_t capacity) +{ + fifo->data = g_new(uint8_t, capacity); + fifo->capacity = capacity; + fifo8_reset(fifo); +} + +void fifo8_destroy(Fifo8 *fifo) +{ + g_free(fifo->data); +} + +void fifo8_push(Fifo8 *fifo, uint8_t data) +{ + assert(fifo->num < fifo->capacity); + fifo->data[(fifo->head + fifo->num) % fifo->capacity] = data; + fifo->num++; +} + +void fifo8_push_all(Fifo8 *fifo, const uint8_t *data, uint32_t num) +{ + uint32_t start, avail; + + assert(fifo->num + num <= fifo->capacity); + + start = (fifo->head + fifo->num) % fifo->capacity; + + if (start + num <= fifo->capacity) { + memcpy(&fifo->data[start], data, num); + } else { + avail = fifo->capacity - start; + memcpy(&fifo->data[start], data, avail); + memcpy(&fifo->data[0], &data[avail], num - avail); + } + + fifo->num += num; +} + +uint8_t fifo8_pop(Fifo8 *fifo) +{ + uint8_t ret; + + assert(fifo->num > 0); + ret = fifo->data[fifo->head++]; + fifo->head %= fifo->capacity; + fifo->num--; + return ret; +} + +uint8_t fifo8_peek(Fifo8 *fifo) +{ + assert(fifo->num > 0); + return fifo->data[fifo->head]; +} + +static const uint8_t *fifo8_peekpop_bufptr(Fifo8 *fifo, uint32_t max, + uint32_t skip, uint32_t *numptr, + bool do_pop) +{ + uint8_t *ret; + uint32_t num, head; + + assert(max > 0 && max <= fifo->num); + assert(skip <= fifo->num); + head = (fifo->head + skip) % fifo->capacity; + num = MIN(fifo->capacity - head, max); + ret = &fifo->data[head]; + + if (do_pop) { + fifo->head = head + num; + fifo->head %= fifo->capacity; + fifo->num -= num; + } + if (numptr) { + *numptr = num; + } + return ret; +} + +const uint8_t *fifo8_peek_bufptr(Fifo8 *fifo, uint32_t max, uint32_t *numptr) +{ + return fifo8_peekpop_bufptr(fifo, max, 0, numptr, false); +} + +const uint8_t *fifo8_pop_bufptr(Fifo8 *fifo, uint32_t max, uint32_t *numptr) +{ + return fifo8_peekpop_bufptr(fifo, max, 0, numptr, true); +} + +static uint32_t fifo8_peekpop_buf(Fifo8 *fifo, uint8_t *dest, uint32_t destlen, + bool do_pop) +{ + const uint8_t *buf; + uint32_t n1, n2 = 0; + uint32_t len; + + if (destlen == 0) { + return 0; + } + + len = destlen; + buf = fifo8_peekpop_bufptr(fifo, len, 0, &n1, do_pop); + if (dest) { + memcpy(dest, buf, n1); + } + + /* Add FIFO wraparound if needed */ + len -= n1; + len = MIN(len, fifo8_num_used(fifo)); + if (len) { + buf = fifo8_peekpop_bufptr(fifo, len, do_pop ? 0 : n1, &n2, do_pop); + if (dest) { + memcpy(&dest[n1], buf, n2); + } + } + + return n1 + n2; +} + +uint32_t fifo8_pop_buf(Fifo8 *fifo, uint8_t *dest, uint32_t destlen) +{ + return fifo8_peekpop_buf(fifo, dest, destlen, true); +} + +uint32_t fifo8_peek_buf(Fifo8 *fifo, uint8_t *dest, uint32_t destlen) +{ + return fifo8_peekpop_buf(fifo, dest, destlen, false); +} + +void fifo8_drop(Fifo8 *fifo, uint32_t len) +{ + len -= fifo8_pop_buf(fifo, NULL, len); + assert(len == 0); +} + +bool fifo8_is_empty(Fifo8 *fifo) +{ + return (fifo->num == 0); +} + +bool fifo8_is_full(Fifo8 *fifo) +{ + return (fifo->num == fifo->capacity); +} + +uint32_t fifo8_num_free(Fifo8 *fifo) +{ + return fifo->capacity - fifo->num; +} + +uint32_t fifo8_num_used(Fifo8 *fifo) +{ + return fifo->num; +} + +const VMStateDescription vmstate_fifo8 = { + .name = "Fifo8", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_VBUFFER_UINT32(data, Fifo8, 1, NULL, capacity), + VMSTATE_UINT32(head, Fifo8), + VMSTATE_UINT32(num, Fifo8), + VMSTATE_END_OF_LIST() + } +}; diff --git a/lib/Devices/fifo8.h b/lib/Devices/fifo8.h new file mode 100644 index 0000000..4f768d4 --- /dev/null +++ b/lib/Devices/fifo8.h @@ -0,0 +1,230 @@ +#ifndef QEMU_FIFO8_H +#define QEMU_FIFO8_H + + +typedef struct { + /* All fields are private */ + uint8_t *data; + uint32_t capacity; + uint32_t head; + uint32_t num; +} Fifo8; + +/** + * fifo8_create: + * @fifo: struct Fifo8 to initialise with new FIFO + * @capacity: capacity of the newly created FIFO + * + * Create a FIFO of the specified capacity. Clients should call fifo8_destroy() + * when finished using the fifo. The FIFO is initially empty. + */ +void fifo8_create(Fifo8 *fifo, uint32_t capacity); + +/** + * fifo8_destroy: + * @fifo: FIFO to cleanup + * + * Cleanup a FIFO created with fifo8_create(). Frees memory created for FIFO + * storage. The FIFO is no longer usable after this has been called. + */ +void fifo8_destroy(Fifo8 *fifo); + +/** + * fifo8_push: + * @fifo: FIFO to push to + * @data: data byte to push + * + * Push a data byte to the FIFO. Behaviour is undefined if the FIFO is full. + * Clients are responsible for checking for fullness using fifo8_is_full(). + */ +void fifo8_push(Fifo8 *fifo, uint8_t data); + +/** + * fifo8_push_all: + * @fifo: FIFO to push to + * @data: data to push + * @num: number of bytes to push + * + * Push a byte array to the FIFO. Behaviour is undefined if the FIFO is full. + * Clients are responsible for checking the space left in the FIFO using + * fifo8_num_free(). + */ +void fifo8_push_all(Fifo8 *fifo, const uint8_t *data, uint32_t num); + +/** + * fifo8_pop: + * @fifo: fifo to pop from + * + * Pop a data byte from the FIFO. Behaviour is undefined if the FIFO is empty. + * Clients are responsible for checking for emptyness using fifo8_is_empty(). + * + * Returns: The popped data byte. + */ +uint8_t fifo8_pop(Fifo8 *fifo); + +/** + * fifo8_peek: + * @fifo: fifo to peek from + * + * Peek the data byte at the current head of the FIFO. Clients are responsible + * for checking for emptyness using fifo8_is_empty(). + * + * Returns: The peeked data byte. + */ +uint8_t fifo8_peek(Fifo8 *fifo); + +/** + * fifo8_pop_buf: + * @fifo: FIFO to pop from + * @dest: the buffer to write the data into (can be NULL) + * @destlen: size of @dest and maximum number of bytes to pop + * + * Pop a number of elements from the FIFO up to a maximum of @destlen. + * The popped data is copied into the @dest buffer. + * Care is taken when the data wraps around in the ring buffer. + * + * Returns: number of bytes popped. + */ +uint32_t fifo8_pop_buf(Fifo8 *fifo, uint8_t *dest, uint32_t destlen); + +/** + * fifo8_peek_buf: + * @fifo: FIFO to read from + * @dest: the buffer to write the data into (can be NULL) + * @destlen: size of @dest and maximum number of bytes to peek + * + * Peek a number of elements from the FIFO up to a maximum of @destlen. + * The peeked data is copied into the @dest buffer. + * Care is taken when the data wraps around in the ring buffer. + * + * Returns: number of bytes peeked. + */ +uint32_t fifo8_peek_buf(Fifo8 *fifo, uint8_t *dest, uint32_t destlen); + +/** + * fifo8_pop_bufptr: + * @fifo: FIFO to pop from + * @max: maximum number of bytes to pop + * @numptr: pointer filled with number of bytes returned (can be NULL) + * + * New code should prefer to use fifo8_pop_buf() instead of fifo8_pop_bufptr(). + * + * Pop a number of elements from the FIFO up to a maximum of @max. The buffer + * containing the popped data is returned. This buffer points directly into + * the internal FIFO backing store and data (without checking for overflow!) + * and is invalidated once any of the fifo8_* APIs are called on the FIFO. + * + * The function may return fewer bytes than requested when the data wraps + * around in the ring buffer; in this case only a contiguous part of the data + * is returned. + * + * The number of valid bytes returned is populated in *@numptr; will always + * return at least 1 byte. max must not be 0 or greater than the number of + * bytes in the FIFO. + * + * Clients are responsible for checking the availability of requested data + * using fifo8_num_used(). + * + * Returns: A pointer to popped data. + */ +const uint8_t *fifo8_pop_bufptr(Fifo8 *fifo, uint32_t max, uint32_t *numptr); + +/** + * fifo8_peek_bufptr: read upto max bytes from the fifo + * @fifo: FIFO to read from + * @max: maximum number of bytes to peek + * @numptr: pointer filled with number of bytes returned (can be NULL) + * + * Peek into a number of elements from the FIFO up to a maximum of @max. + * The buffer containing the data peeked into is returned. This buffer points + * directly into the FIFO backing store. Since data is invalidated once any + * of the fifo8_* APIs are called on the FIFO, it is the caller responsibility + * to access it before doing further API calls. + * + * The function may return fewer bytes than requested when the data wraps + * around in the ring buffer; in this case only a contiguous part of the data + * is returned. + * + * The number of valid bytes returned is populated in *@numptr; will always + * return at least 1 byte. max must not be 0 or greater than the number of + * bytes in the FIFO. + * + * Clients are responsible for checking the availability of requested data + * using fifo8_num_used(). + * + * Returns: A pointer to peekable data. + */ +const uint8_t *fifo8_peek_bufptr(Fifo8 *fifo, uint32_t max, uint32_t *numptr); + +/** + * fifo8_drop: + * @fifo: FIFO to drop bytes + * @len: number of bytes to drop + * + * Drop (consume) bytes from a FIFO. + */ +void fifo8_drop(Fifo8 *fifo, uint32_t len); + +/** + * fifo8_reset: + * @fifo: FIFO to reset + * + * Reset a FIFO. All data is discarded and the FIFO is emptied. + */ +void fifo8_reset(Fifo8 *fifo); + +/** + * fifo8_is_empty: + * @fifo: FIFO to check + * + * Check if a FIFO is empty. + * + * Returns: True if the fifo is empty, false otherwise. + */ +bool fifo8_is_empty(Fifo8 *fifo); + +/** + * fifo8_is_full: + * @fifo: FIFO to check + * + * Check if a FIFO is full. + * + * Returns: True if the fifo is full, false otherwise. + */ +bool fifo8_is_full(Fifo8 *fifo); + +/** + * fifo8_num_free: + * @fifo: FIFO to check + * + * Return the number of free bytes in the FIFO. + * + * Returns: Number of free bytes. + */ +uint32_t fifo8_num_free(Fifo8 *fifo); + +/** + * fifo8_num_used: + * @fifo: FIFO to check + * + * Return the number of used bytes in the FIFO. + * + * Returns: Number of used bytes. + */ +uint32_t fifo8_num_used(Fifo8 *fifo); + +extern const VMStateDescription vmstate_fifo8; + +#define VMSTATE_FIFO8_TEST(_field, _state, _test) { \ + .name = (stringify(_field)), \ + .field_exists = (_test), \ + .size = sizeof(Fifo8), \ + .vmsd = &vmstate_fifo8, \ + .flags = VMS_STRUCT, \ + .offset = vmstate_offset_value(_state, _field, Fifo8), \ +} + +#define VMSTATE_FIFO8(_field, _state) \ + VMSTATE_FIFO8_TEST(_field, _state, NULL) + +#endif /* QEMU_FIFO8_H */ diff --git a/lib/Devices/ns16550.rb b/lib/Devices/ns16550.rb index 7c7f9f2..d3d8630 100644 --- a/lib/Devices/ns16550.rb +++ b/lib/Devices/ns16550.rb @@ -1,77 +1,101 @@ -require_relative "../SDL/plod" +require_relative '../SDL/plod' -module Devices +module System - Device(:ns16550) { - - Register(:rbr) { - size 0x1 - offset 0x0 - type :ro - enableIf { - lsr.dlab - } - } - - Register(:thr) { - size 0x1 - offset 0x0 - type :wo - } - - Register(:ier) { - size 0x1 - offset 0x1 + Struct(:fifo8) { + Method(:init) { + size[]= 0 } - Register(:iir) { - size 0x1 - offset 0x2 - type :ro + Method(:push, value: b8) { + buf.set(size, value) + size[]= size + 1 } - Register(:fcr) { - size 0x1 - offset 0x2 - type :wo - } - - Register(:lcr) { - size 0x1 - offset 0x3 - } - - Register(:mcr) { - size 0x1 - offset 0x4 - } + Method(pop, ret: b8) { + Var b8, valToRet - Register(:lsr) { - size 0x1 - offset 0x5 - field :dlab, 0x7 - } - - Register(:msr) { - size 0x1 - offset 0x6 - } - - Register(:scr) { - size 0x1 - offset 0x7 - } - - Register(:dll) { - size 0x1 - offset 0x0 - } - - Register(:dlm) { - size 0x1 - offset 0x1 + valToRet[]= buf[size - 1] + size[]= size - 1 + ret valToRet } + Field(:buf, array(b8, 8)) + Field(:size, int) } + # Device(:ns16550) { + + # Register(:rbr) { + # size 0x1 + # offset 0x0 + # type :ro + # enableIf { lsr.dlab == 0 } + # } + + # Register(:thr) { + # size 0x1 + # offset 0x0 + # type :wo + # enableIf { lsr.dlab == 0 } + # } + + # Register(:ier) { + # size 0x1 + # offset 0x1 + # enableIf { lsr.dlab == 0 } + # } + + # Register(:iir) { + # size 0x1 + # offset 0x2 + # type :ro + # } + + # Register(:fcr) { + # size 0x1 + # offset 0x2 + # type :wo + # } + + # Register(:lcr) { + # size 0x1 + # offset 0x3 + # } + + # Register(:mcr) { + # size 0x1 + # offset 0x4 + # } + + # Register(:lsr) { + # size 0x1 + # offset 0x5 + # field :dlab, 0x7 + # } + + # Register(:msr) { + # size 0x1 + # offset 0x6 + # } + + # Register(:scr) { + # size 0x1 + # offset 0x7 + # } + + # Register(:dll) { + # size 0x1 + # offset 0x0 + # enableIf { lsr.dlab == 1 } + # } + + # Register(:dlm) { + # size 0x1 + # offset 0x1 + # enableIf { lsr.dlab == 1 } + # } + + # } + end diff --git a/lib/Devices/serial.h b/lib/Devices/serial.h new file mode 100644 index 0000000..ea82ffa --- /dev/null +++ b/lib/Devices/serial.h @@ -0,0 +1,88 @@ +/* + * QEMU 16550A UART emulation + * + * Copyright (c) 2003-2004 Fabrice Bellard + * Copyright (c) 2008 Citrix Systems, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef HW_SERIAL_H +#define HW_SERIAL_H + +#include "hw/core/qdev.h" +#include "chardev/char-fe.h" +#include "system/memory.h" +#include "qemu/fifo8.h" +#include "qom/object.h" + +#define UART_FIFO_LENGTH 16 /* 16550A Fifo Length */ + +struct SerialState { + DeviceState parent; + + uint16_t divider; + uint8_t rbr; /* receive register */ + uint8_t thr; /* transmit holding register */ + uint8_t tsr; /* transmit shift register */ + uint8_t ier; + uint8_t iir; /* read only */ + uint8_t lcr; + uint8_t mcr; + uint8_t lsr; /* read only */ + uint8_t msr; /* read only */ + uint8_t scr; + uint8_t fcr; + uint8_t fcr_vmstate; /* we can't write directly this value + it has side effects */ + /* NOTE: this hidden state is necessary for tx irq generation as + it can be reset while reading iir */ + int thr_ipending; + qemu_irq irq; + CharFrontend chr; + int last_break_enable; + uint32_t baudbase; + uint32_t tsr_retry; + guint watch_tag; + bool wakeup; + + /* Time when the last byte was successfully sent out of the tsr */ + uint64_t last_xmit_ts; + Fifo8 recv_fifo; + Fifo8 xmit_fifo; + /* Interrupt trigger level for recv_fifo */ + uint8_t recv_fifo_itl; + + QEMUTimer *fifo_timeout_timer; + int timeout_ipending; /* timeout interrupt pending state */ + + uint64_t char_transmit_time; /* time to transmit a char in ticks */ + int poll_msl; + + QEMUTimer *modem_status_poll; + MemoryRegion io; +}; + +extern const VMStateDescription vmstate_serial; +extern const MemoryRegionOps serial_io_ops; + +#define TYPE_SERIAL "serial" +OBJECT_DECLARE_SIMPLE_TYPE(SerialState, SERIAL) + +#endif diff --git a/lib/SDL/plod.rb b/lib/SDL/plod.rb index 65e890c..b2c9b17 100644 --- a/lib/SDL/plod.rb +++ b/lib/SDL/plod.rb @@ -1,49 +1,76 @@ require 'Common/scope' +require 'Common/type' + +module Protea + module Plod + # Dumy + class BuilderUtils + def array(type, size) + Type::Array.new(type, size) + end + + def int + Type::Int.new + end + + def method_missing(name) + if name.start_with?("b") + size = name[1..].to_i + Type::Bitvector.new(size) + else + raise "Bad type: #{name}" + end + end + end + end +end -module Devices - @@desc = { - devices: [] +module System + @desc = { + devices: [], + structures: [] } - @@semablocks = [] + @semablocks = [] + @gscope = {} - def self.getSemablocks - @@semablocks + def self.desc + @desc end - def self.initScope + def self.semablocks + @semablocks + end + + def self.init_scope(dscope, lscope) scope = Protea::Scope.new(nil) - @@desc[:devices][0][:registers].each do |reg| - regvar = scope.method(reg[:name], :regref) - reg[:fields].each do |field| - regvar.define_singleton_method(field[:name]) do - @scope.get_field(self, field[:lsb], ('b' + field[:size].to_s).to_sym) - end + add_vscope = lambda do |vscope| + vscope.each do |name, var| + scope.insert_var(name, var) end end - - return scope - end - def self.processSemablocks - @@semablocks.each do |block| - scope = initScope - scope.instance_eval(&block[2]) - block[0][block[1]] = scope.to_h - end + add_vscope.call(@gscope) + add_vscope.call(dscope) + add_vscope.call(lscope) - @@semablocks = [] + scope end - def self.getDesc - @@desc + def self.process_semablocks + @semablocks.each do |block| + scope = init_scope(block[:dscope], block[:lscope]) + scope.instance_eval(&block[:block]) + block[:body] << scope.to_h + end end - class RegBuilder + class RegBuilder < Protea::Plod::BuilderUtils attr_accessor :info def initialize(name) + super() @info = { name: name, fields: [] } end @@ -60,7 +87,7 @@ def type(value) end def enableIf(&block) - Devices.getSemablocks << [@info, :enableIf, block] + System.getSemablocks << [@info, :enableIf, block] end def field(name, lsb, size = 1) @@ -68,26 +95,76 @@ def field(name, lsb, size = 1) end end - class DeviceBuilder + class DeviceBuilder < Protea::Plod::BuilderUtils attr_accessor :info def initialize(name) + super() @info = { name: name, registers: [] } end def Register(name, &block) - regBuilder = RegBuilder.new(name) - regBuilder.instance_eval(&block) - @info[:registers] << regBuilder.info + reg_builder = RegBuilder.new(name) + reg_builder.instance_eval(&block) + @info[:registers] << reg_builder.info nil end end def self.Device(name, &block) - deviceBuilder = DeviceBuilder.new(name) - deviceBuilder.instance_eval(&block) - @@desc[:devices] << deviceBuilder.info - Devices.processSemablocks + device_builder = DeviceBuilder.new(name) + device_builder.instance_eval(&block) + @desc[:devices] << device_builder.info + nil + end + + class StructBuilder < Protea::Plod::BuilderUtils + attr_accessor :info + + def initialize(name) + super() + @info = { name: name, methods: {}, fields: {} } + @dscope = {} + end + + def Method(name, **args, &block) + @info[:methods][name] = { args: args, body: [] } + + lscope = {} + args.each do |name, type| + lscope[name] = Protea::Var.new(nil, name, type.name, nil, type) + args[name] = type.to_s + end + + System.semablocks << { dscope: @dscope, lscope: lscope, body: @info[:methods][name][:body], block: block } + nil + end + + def Field(name, type) + # puts type + # puts type.to_str + raise "Invalid type: #{type}" unless type.is_a?(Protea::Type::TypeObject) + + @dscope[name] = Protea::Var.new(nil, name, type.name, nil, type) + @info[:fields][name] = type.to_s + end + end + + def self.Struct(name, &block) + struct_builder = StructBuilder.new(name) + struct_builder.instance_eval(&block) + @desc[:structures] << struct_builder.info nil end + end + + # @@desc[:devices][0][:registers].each do |reg| + # regvar = scope.method(reg[:name], :regref) + + # reg[:fields].each do |field| + # regvar.define_singleton_method(field[:name]) do + # @scope.get_field(self, field[:lsb], ('b' + field[:size].to_s).to_sym) + # end + # end + # end diff --git a/lib/d_ir_gen.rb b/lib/d_ir_gen.rb index d56ea4a..c97d3b7 100644 --- a/lib/d_ir_gen.rb +++ b/lib/d_ir_gen.rb @@ -4,4 +4,6 @@ require 'Devices/ns16550' -puts Devices.getDesc.to_yaml +System.process_semablocks + +puts System.desc.to_yaml