paiza.ioでelixir その292

# 概要
paiza.ioでelixirやってみた。
高位合成、やってみた。
俺言語を拡張してLチカを実現した。

# 俺言語拡張仕様
変数o, p, qは、入力でスイッチがつながってる。１でON、０でOFF
変数x, y, zは、出力でLEDがつながってる。１で点灯、０で消灯

ビット演算子を採用する。
 ~  : NOT
 &  : AND
 |  : OR
 ^  : XOR

# 片方がONなら、点灯する、俺言語コード
o, pは、入力でスイッチがつながってる。
xは、出力でLEDがつながってる。
o, pが、1,0か0,1なら、点灯する。
例：階段の上下にあるスイッチ。
```
10 a=o^p
20 #=a@1*50
30 x=0
40 #=10
50 x=1
60 #=10
```

# 高位合成するelixirコード

```

defmodule Asm do
	def start_link do
		Agent.start_link(fn ->
			%{u: ""}
		end, name: __MODULE__)
	end
    def a(a, b) do
		Agent.update(__MODULE__, fn v ->
		    c = String.to_integer(a) + 10
			str = "            " <> a <> ":\n" <>
			    "            begin\n" <>
			    "                a <= " <> b <> ";\n" <>
			    "                pc <= #{c};\n" <>
			    "            end\n"
			%{v | u: v.u <> str}
		end)
    end
    def o(a, b) do
		Agent.update(__MODULE__, fn v ->
		    c = String.to_integer(a) + 10
			str = "            " <> a <> ":\n" <>
			    "            begin\n" <>
			    "                o <= " <> b <> ";\n" <>
			    "                pc <= #{c};\n" <>
			    "            end\n"
			%{v | u: v.u <> str}
		end)
    end
    def p(a, b) do
		Agent.update(__MODULE__, fn v ->
		    c = String.to_integer(a) + 10
			str = "            " <> a <> ":\n" <>
			    "            begin\n" <>
			    "                p <= " <> b <> ";\n" <>
			    "                pc <= #{c};\n" <>
			    "            end\n"
			%{v | u: v.u <> str}
		end)
    end
    def x(a, b) do
		Agent.update(__MODULE__, fn v ->
		    c = String.to_integer(a) + 10
			str = "            " <> a <> ":\n" <>
			    "            begin\n" <>
			    "                x <= " <> b <> ";\n" <>
			    "                pc <= #{c};\n" <>
			    "            end\n"
			%{v | u: v.u <> str}
		end)
    end
    def b(a, b) do
		Agent.update(__MODULE__, fn v ->
		    c = String.to_integer(a) + 10
			str = "            " <> a <> ":\n" <>
			    "            begin\n" <>
			    "                b <= " <> b <> ";\n" <>
			    "                pc <= #{c};\n" <>
			    "            end\n"
			%{v | u: v.u <> str}
		end)
    end
    def print(a, b) do
		Agent.update(__MODULE__, fn v ->
            c = String.to_integer(a)
			if String.match?(b, ~r/\"/) do
			    b = String.replace(b, "\"", "")
		        s = String.split(b, "", trim: true)
                l = String.length(b)
			    str = Enum.reduce(s, ["", c], fn x, y ->
			        pc = Enum.at(y, 1)
			        str = Enum.at(y, 0) <> 
			            "            #{pc}:\n" <>
			            "            begin\n" <>
                        "                out <= \"#{x}\";\n" <>
                        "                pc <= #{pc + 1};\n" <>
                        "            end\n"
                    pc = pc + 1
                    [str, pc]
			    end)
			    str = Enum.at(str, 0) <>
    			    "            #{c + l}:\n" <>
		            "            begin\n" <>
                    "                out <= \" \";\n" <>
                    "                pc <= #{c + 10};\n" <>
                    "            end\n"
			    %{v | u: v.u <> str}
			else
			    str = "            #{c}:\n" <>
			    "            begin\n" <>
                "                out <= a / 10 + 48;\n" <>
                "                pc <= #{c + 1};\n" <>
                "            end\n" <>
                "            #{c + 1}:\n" <>
                "            begin\n" <>
                "                out <= a % 10 + 48;\n" <>
                "                pc <= #{c + 2};\n" <>
                "            end\n" <>
                "            #{c + 2}:\n" <>
                "            begin\n" <>
                "                out <= 32;\n" <>
                "                pc <= #{c + 10};\n" <>
                "            end\n"
                %{v | u: v.u <> str}
			end
		end)
    end
    def loop(a, b) do
		Agent.update(__MODULE__, fn v ->
		    c = String.to_integer(a) + 10
			if String.match?(b, ~r/\*/) do
			    b = String.replace(b, "@", "==")
		        s = String.split(b, "*")
			    str = "            " <> a <> ":\n" <>
			    "            begin\n" <>
			    "                if (" <> Enum.at(s, 0) <> ")\n" <>
			    "                    pc <= " <> Enum.at(s, 1) <> ";\n" <>
			    "                else\n" <>
                "                    pc <= #{c};\n" <>
			    "            end\n"
			    %{v | u: v.u <> str}
		    else
		        str = "            " <> a <> ":\n" <>
		        "            begin\n" <>
		        "                pc <= " <> b <> ";\n" <>
		        "            end\n"
		        %{v | u: v.u <> str}
		    end
		end)
    end
    def make() do
		Agent.get(__MODULE__, fn v ->
            IO.puts "module ore(input clk, input rst, input o, input p, input q, output x, output y, output z);\n" <>
	        "    reg [7:0] a;\n" <>
	        "    reg [7:0] b;\n" <>
	        "    reg [7:0] pc;\n" <>
	        "    reg x;\n" <>
	        "    reg y;\n" <>
	        "    reg z;\n" <>
	        "    wire o;\n" <>
	        "    wire p;\n" <>
	        "    wire q;\n" <>
	        "    always @(posedge clk)\n" <>
	        "    begin\n" <>
		    "        if (!rst)\n" <>
		    "        begin\n" <>
			"            a <= 0;\n" <>
			"            b <= 0;\n" <>
			"            pc <= 10;\n" <>
		    "        end\n" <>
		    "        else\n" <>
	        "        begin\n" <>
		    "            case(pc)\n" <>
		    v.u <>
		    "            endcase\n" <>
	        "        end\n" <>
	        "    end\n" <>
	        "    initial\n" <>
	        "    begin\n" <>
		    "        //$monitor(\" %d  %s\", pc, out);\n" <>
	        "    end\n" <>
		    "endmodule\n\n" <>
            "module test;\n" <>
	        "    reg clk,\n" <>
		    "        rst;\n" <>
	        "    wire [7:0] out;\n" <>
	        "    reg o;\n" <>
	        "    reg p;\n" <>
	        "    reg q;\n" <>
	        "    wire x;\n" <>
	        "    wire y;\n" <>
	        "    wire z;\n" <>
	        "    ore u(.clk(clk), .rst(rst), .o(o), .p(p), .q(q), .x(x), .y(y), .z(z));\n" <>
	        "    initial\n" <>
	        "    begin\n" <>
		    "        clk = 0;\n" <>
		    "        rst = 1;\n" <>	
		    "    #2\n" <>
			"        rst = 0;\n" <>
		    "    #2\n" <>
		    "        rst = 1;\n" <>
		    "    #20\n" <>
		    "        o = 0;\n" <>
		    "        p = 0;\n" <>
		    "    #20\n" <>
		    "        o = 1;\n" <>
		    "        p = 0;\n" <>
		    "    #20\n" <>
		    "        o = 0;\n" <>
		    "        p = 0;\n" <>
		    "    #20\n" <>
		    "        o = 0;\n" <>
		    "        p = 1;\n" <>
		    "    #20\n" <>
		    "        o = 0;\n" <>
		    "        p = 0;\n" <>
		    "    #20\n" <>
		    "        o = 1;\n" <>
		    "        p = 1;\n" <>
		    "    #20\n" <>
		    "        o = 0;\n" <>
		    "        p = 0;\n" <>
		    "    #20\n" <>
			"        $finish;\n" <>
	        "    end\n" <>
	        "    always\n" <>
		    "    #1\n" <>
			"        clk = ~clk;\n" <>
	        "    initial\n" <>
	        "    begin\n" <>
	        "        $display(\" o  p  x \");\n" <>
		    "        $monitor(\" %d %d  %d \", o, p, x);\n" <>
	        "    end\n" <>
            "endmodule\n"
		end)
	end
end

defmodule Main do
	def run(str) do
	    str = String.replace(str, "=", " ")
    	Enum.map(String.split(str, "\n"), fn l ->
		    s = String.split(l, " ")
		    cond do
			Enum.at(s, 1) == "?" ->
				Asm.print(Enum.at(s, 0), Enum.at(s, 2))
			Enum.at(s, 1) == "#" ->
				Asm.loop(Enum.at(s, 0), Enum.at(s, 2))
			Enum.at(s, 1) == "a" ->
				Asm.a(Enum.at(s, 0), Enum.at(s, 2))
			Enum.at(s, 1) == "o" ->
				Asm.o(Enum.at(s, 0), Enum.at(s, 2))
			Enum.at(s, 1) == "p" ->
				Asm.p(Enum.at(s, 0), Enum.at(s, 2))
			Enum.at(s, 1) == "x" ->
				Asm.x(Enum.at(s, 0), Enum.at(s, 2))
			Enum.at(s, 1) == "b" ->
				Asm.b(Enum.at(s, 0), Enum.at(s, 2))
			true ->
				IO.puts l
			end
	    end)
	    Asm.make
	end
end

Asm.start_link

Main.run("""
10 a=o^p
20 #=a@1*50
30 x=0
40 #=10
50 x=1
60 #=10
""")




```

# 実行結果、生成されたverilogコード
テストベンチも生成してる。

```

module ore(input clk, input rst, input o, input p, input q, output x, output y, output z);
    reg [7:0] a;
    reg [7:0] b;
    reg [7:0] pc;
    reg x;
    reg y;
    reg z;
    wire o;
    wire p;
    wire q;
    always @(posedge clk)
    begin
        if (!rst)
        begin
            a <= 0;
            b <= 0;
            pc <= 10;
        end
        else
        begin
            case(pc)
            10:
            begin
                a <= o^p;
                pc <= 20;
            end
            20:
            begin
                if (a==1)
                    pc <= 50;
                else
                    pc <= 30;
            end
            30:
            begin
                x <= 0;
                pc <= 40;
            end
            40:
            begin
                pc <= 10;
            end
            50:
            begin
                x <= 1;
                pc <= 60;
            end
            60:
            begin
                pc <= 10;
            end
            endcase
        end
    end
    initial
    begin
        //$monitor(" %d  %s", pc, out);
    end
endmodule

module test;
    reg clk,
        rst;
    wire [7:0] out;
    reg o;
    reg p;
    reg q;
    wire x;
    wire y;
    wire z;
    ore u(.clk(clk), .rst(rst), .o(o), .p(p), .q(q), .x(x), .y(y), .z(z));
    initial
    begin
        clk = 0;
        rst = 1;
    #2
        rst = 0;
    #2
        rst = 1;
    #20
        o = 0;
        p = 0;
    #20
        o = 1;
        p = 0;
    #20
        o = 0;
        p = 0;
    #20
        o = 0;
        p = 1;
    #20
        o = 0;
        p = 0;
    #20
        o = 1;
        p = 1;
    #20
        o = 0;
        p = 0;
    #20
        $finish;
    end
    always
    #1
        clk = ~clk;
    initial
    begin
        $display(" o  p  x ");
        $monitor(" %d  %d   %d ", o, p, x);
    end
endmodule

```

# 生成されたverilogコードのシュミレーション結果

```
>vvp a.out
 o  p  x
 x  x   x
 x  x   0
 0  0   0
 1  0   0
 1  0   1
 0  0   1
 0  0   0
 0  1   0
 0  1   1
 0  0   1
 0  0   0
 1  1   0
 0  0   0
led0.v:103: $finish called at 164 (1s)
```

# 成果物

https://paiza.io/projects/MMohSvZxpgZWBzn6_ed9pQ

以上。