amaranth-lang / amaranth

A modern hardware definition language and toolchain based on Python
https://amaranth-lang.org/docs/amaranth/
BSD 2-Clause "Simplified" License
1.54k stars 170 forks source link

How to adding a new component ? #857

Closed jalcim closed 1 year ago

jalcim commented 1 year ago

On the documentation and on the code i didn't find the way for adding a new component.

only find these informations #781 #785

other question, where can i find the list of component ?

whitequark commented 1 year ago

It is unclear what are you asking. Can you rephrase your question with more detail?

jalcim commented 1 year ago

on this exemple we use a Dlatch. i want to know where this component is defined, and how add another one.

def elaborate(self, platform): m = Module() m.d.comb += self.ovf.eq(self.count == self.limit)

image

https://amaranth-lang.org/docs/amaranth/latest/intro.html#the-amaranth-standard-library

whitequark commented 1 year ago

What kind of component are you aiming to add?

jalcim commented 1 year ago

All and nothing i work with a friend who start to use amaranth, and i provide him verilog modules, we want know how he can use it.

For example i want send him a counter, or multiplexer, or usb controler xD

jalcim commented 1 year ago

all what i find is that. https://github.com/amaranth-lang/amaranth/blob/main/amaranth/hdl/dsl.py#L70

they have no documentation about standard-library.

whitequark commented 1 year ago

The documentation for language constructs is here, though it's not complete at the moment.

jalcim commented 1 year ago

The documentation for language constructs is here, though it's not complete at the moment.

I know i have read it. I wanna know how to add a new one. I have read all the documentation, and search in the source code.

whitequark commented 1 year ago

Adding new language constructs (under amaranth.hdl.*) is an advanced topic that we aren't going to cover in the documentation even when it's complete. Unless you give more details about what you're trying to achieve I'm afraid you're on your own.

jalcim commented 1 year ago

Its not a problem for us to contribute ! I didn't ask you to do it for us. we want understand how to do it.

Maybe after can we adding it to the documentation. But... i feel you just didn't want to respond, and i didn't understand why.

We want add some module inside amaranth, and we search how to do it, because its not a simple software architecture.

Maybe can you provide me a starting point ? For exemple m.d.comb.

Because without that is to limited, i didn't understand why this part is hide...

whitequark commented 1 year ago

If you'd like to extend the language you'll need to go through the RFC process. In general, I prefer to avoid having incompatible language forks around, so making it easier to make an incompatible language fork isn't something that I want to make easier.

jalcim commented 1 year ago

Then you refuse to help me, because you'r scary i fork your project ?

adamgreig commented 1 year ago

@jalcim, are you asking how to use an existing verilog module in an Amaranth design, or perhaps how to write a module in Amaranth? I'm not sure if you're asking how to add a whole new concept to the language, or just how to use the language for a normal project without having to modify the language itself.

jalcim commented 1 year ago

Its a good question ! At this step i didn't know and explore all the way ! The must simple is to use a verilog module inside amaranth, because i see its hard to write a module in amaranth.

adamgreig commented 1 year ago

Maybe some examples would help. Here's an example of writing a simple counter in Amaranth as a module and using it, and also including another counter written in Verilog, including it, instantiating it as a module, and using it.

import amaranth as am
from amaranth_boards.icebreaker import ICEBreakerPlatform

class Counter(am.Elaboratable):
    def __init__(self, n):
        # Store an n-bit register for the count value
        self.count = am.Signal(n)

        # Output a single-bit output signal
        self.overflow = am.Signal()

    def elaborate(self, platform):
        m = am.Module()
        # Count up each clock cycle
        m.d.sync += self.count.eq(self.count + 1)
        # Output overflow on cycles where the count is 0
        m.d.comb += self.overflow.eq(self.count == 0)
        return m

class Top(am.Elaboratable):
    def elaborate(self, platform):
        m = am.Module()

        # Instantiate the Amaranth counter
        m.submodules.counter = counter = Counter(16)
        # Connect its output to an LED
        m.d.comb += platform.request("led_g", 0).o.eq(counter.overflow)

        # Add some Verilog
        v = """
        module verilog_counter ( clk, cnt );
            parameter WIDTH = 8;
            input clk;
            output reg [WIDTH-1:0] cnt;

            always @(posedge clk) begin
                cnt <= cnt + 1;
            end
        endmodule
        """
        platform.add_file("counter.v", v)
        count = am.Signal(8)
        m.submodules.verilog_counter = am.Instance(
            "verilog_counter",
            p_WIDTH=count.width,
            i_clk=am.ClockSignal(),
            o_cnt=count,
        )

        # Connect the top bit of its count value to another LED.
        m.d.comb += platform.request("led_r", 0).o.eq(count[-1])

        return m

if __name__ == "__main__":
    top = Top()
    plat = ICEBreakerPlatform()
    plat.build(top)
jalcim commented 1 year ago

ok then

  1. instantiate a submodule with am.Instance and set parameter, input, output
  2. use directly the output

(I use skywater, then no platform)

amaranth file

import os

import amaranth as am
from amaranth import Elaboratable, Signal
from amaranth.back import verilog, rtlil
from amaranth.hdl import ir

class Top(am.Elaboratable):
    def __init__(self):
        # Store an n-bit register for the count value                                                                                                                                                                                         
        self.clk = am.Signal(1)

        # Output a single-bit output signal                                                                                                                                                                                                   
        self.cnt = am.Signal(8)

    def elaborate(self, platform):
        m = am.Module()

        count = am.Signal(8)
        m.submodules.verilog_counter = am.Instance(
            "verilog_counter",
            p_WIDTH=count.width,
            i_clk=self.clk,
            o_cnt=self.cnt,
        )
        return m

def get_rtlil_code(machin : Elaboratable):
    name = "top"
    platform = None
    ports = [machin.clk, machin.cnt]
    emit_src = True
    strip_internal_attrs = False
    fragment = ir.Fragment.get(machin, platform).prepare(ports=ports)
    rtlil_text, name_map = rtlil.convert_fragment(
        fragment,
        name,
        emit_src=emit_src,
    )
    ENV_USERNAME = os.environ.get("USER")
    rtlil_source_text = rtlil_text.replace(ENV_USERNAME, "user")
    return rtlil_source_text

if __name__ == "__main__":
    machin = Top()

    rtlil_source_text = get_rtlil_code(machin)
    outfile_rtlil = "output_up_counter.rtlil"
    with open(outfile_rtlil, "w") as fd:
        fd.write(rtlil_source_text)
        print(f"'{outfile_rtlil}' filename written.")

verilog file

module verilog_counter ( clk, cnt );
   parameter WIDTH = 8;
   input clk;
   output reg [WIDTH-1:0] cnt;

   always @(posedge clk) begin
      cnt <= cnt + 1;
   end
endmodule

inside yosys cmd

read_rtlil output_up_counter.rtlil
read_verilog count.v
procs
hierarchy
synth
write_spice out.sp
jalcim commented 1 year ago

Thanks you very much for the help !