smolarith


Namesmolarith JSON
Version 0.1.1 PyPI version JSON
download
home_page
SummarySoft-core arithmetic components written in Amaranth HDL
upload_time2024-03-06 04:10:22
maintainer
docs_urlNone
author
requires_python>=3.8
licenseMIT
keywords multiplication division hdl cpu
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # smolarith

[![Documentation Status](https://readthedocs.org/projects/smolarith/badge/?version=latest)](https://smolarith.readthedocs.io/en/latest/?badge=latest)

Small arithmetic soft-cores for smol FPGAs. If your FPGA has hard IP
implementing functions in this repository, you should use those instead.

## Example

```python
from amaranth import signed, Module, C
from amaranth.lib.wiring import Component, Signature, Out, In
from amaranth.back.verilog import convert
from amaranth.sim import Simulator

import sys
from smolarith import mul
from smolarith.mul import MulticycleMul


class Celsius2Fahrenheit(Component):
    """Module to convert Celsius temperatures to Fahrenheit (F = 1.8*C + 32)."""

    def __init__(self, *, qc, qf, scale_const=5):
        self.qc = qc
        self.qf = qf
        self.scale_const = scale_const

        self.c_width = self.qc[0] + self.qc[1]
        self.f_width = self.qf[0] + self.qf[1]
        # 1.8 not representable. 1.78125 will have to be close enough.
        # Q1.{self.scale_const}
        self.mul_factor = C(9*2**self.scale_const // 5)
        # Q6.{self.qc[1] + self.scale_const}
        self.add_factor = C(32 << (self.qc[1] + self.scale_const))
        # Mul result will have self.qc[1] + self.scale_const fractional bits.
        # Adjust to desired Fahrenheit precision.
        self.extra_bits = self.qc[1] + self.scale_const - self.qf[1]

        # Output will be 2*max(self.mul_factor.width, self.c_width)...
        # more bits than we need.
        self.mul = MulticycleMul(width=max(self.mul_factor.width,
                                           self.c_width))

        super().__init__({
            "c": In(Signature({
                "payload": Out(signed(self.c_width)),
                "ready": In(1),
                "valid": Out(1)
            })),
            "f": Out(Signature({
                "payload": Out(signed(self.f_width)),
                "ready": In(1),
                "valid": Out(1)
            }))
        })

    def elaborate(self, plat):
        m = Module()
        m.submodules.mul = self.mul

        m.d.comb += [
            # res = 1.8*C
            self.c.ready.eq(self.mul.inp.ready),
            self.mul.inp.valid.eq(self.c.valid),
            self.mul.inp.payload.a.eq(self.c.payload),
            self.mul.inp.payload.b.eq(self.mul_factor),
            self.mul.inp.payload.sign.eq(mul.Sign.SIGNED_UNSIGNED),

            # F = res + 32, scaled to remove frac bits we don't need.
            self.f.payload.eq((self.mul.outp.payload.o + self.add_factor) >>
                              self.extra_bits),
            self.f.valid.eq(self.mul.outp.valid),
            self.mul.outp.ready.eq(self.f.ready)
        ]

        return m


def sim(*, c2f, start_c, end_c, gtkw=False):
    sim = Simulator(c2f)
    sim.add_clock(1e-6)

    def proc():
        yield c2f.f.ready.eq(1)
        yield

        for i in range(start_c, end_c):
            yield c2f.c.payload.eq(i)
            yield c2f.c.valid.eq(1)
            yield
            yield c2f.c.valid.eq(0)

            # Wait for module to calculate results.
            while (yield c2f.f.valid) != 1:
                yield

            # This is a low-effort attempt to print fixed-point numbers
            # by converting them into floating point.
            print((yield c2f.c.payload) / 2**c2f.qc[1],
                  (yield c2f.f.payload) / 2**c2f.qf[1])

    sim.add_sync_process(proc)

    if gtkw:
        with sim.write_vcd("c2f.vcd", "c2f.gtkw"):
            sim.run()
    else:
        sim.run()


if __name__ == "__main__":
    # See: https://en.wikipedia.org/wiki/Q_(number_format)
    c2f = Celsius2Fahrenheit(qc=(8, 3), qf=(10, 3), scale_const=15)

    if len(sys.argv) > 1 and sys.argv[1] == "sim":
        if len(sys.argv) >= 2:
            start_c = int(float(sys.argv[2]) * 2**c2f.qc[1])
        else:
            start_c = -2**(c2f.qc[0] + c2f.qc[1] - 1)

        if len(sys.argv) >= 3:
            end_c = int(float(sys.argv[3]) * 2**c2f.qc[1])
        else:
            end_c = 2**(c2f.qc[0] + c2f.qc[1] - 1)

        sim(c2f=c2f, start_c=start_c, end_c=end_c, gtkw=True)
    else:
        print(convert(c2f))
```

            

Raw data

            {
    "_id": null,
    "home_page": "",
    "name": "smolarith",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": "",
    "keywords": "multiplication division HDL cpu",
    "author": "",
    "author_email": "William D. Jones <thor0505@comcast.net>",
    "download_url": "https://files.pythonhosted.org/packages/c9/b2/3677dc4a267a0992bb36ccf399552d53cf361f2efeb184166dbf598326ac/smolarith-0.1.1.tar.gz",
    "platform": null,
    "description": "# smolarith\n\n[![Documentation Status](https://readthedocs.org/projects/smolarith/badge/?version=latest)](https://smolarith.readthedocs.io/en/latest/?badge=latest)\n\nSmall arithmetic soft-cores for smol FPGAs. If your FPGA has hard IP\nimplementing functions in this repository, you should use those instead.\n\n## Example\n\n```python\nfrom amaranth import signed, Module, C\nfrom amaranth.lib.wiring import Component, Signature, Out, In\nfrom amaranth.back.verilog import convert\nfrom amaranth.sim import Simulator\n\nimport sys\nfrom smolarith import mul\nfrom smolarith.mul import MulticycleMul\n\n\nclass Celsius2Fahrenheit(Component):\n    \"\"\"Module to convert Celsius temperatures to Fahrenheit (F = 1.8*C + 32).\"\"\"\n\n    def __init__(self, *, qc, qf, scale_const=5):\n        self.qc = qc\n        self.qf = qf\n        self.scale_const = scale_const\n\n        self.c_width = self.qc[0] + self.qc[1]\n        self.f_width = self.qf[0] + self.qf[1]\n        # 1.8 not representable. 1.78125 will have to be close enough.\n        # Q1.{self.scale_const}\n        self.mul_factor = C(9*2**self.scale_const // 5)\n        # Q6.{self.qc[1] + self.scale_const}\n        self.add_factor = C(32 << (self.qc[1] + self.scale_const))\n        # Mul result will have self.qc[1] + self.scale_const fractional bits.\n        # Adjust to desired Fahrenheit precision.\n        self.extra_bits = self.qc[1] + self.scale_const - self.qf[1]\n\n        # Output will be 2*max(self.mul_factor.width, self.c_width)...\n        # more bits than we need.\n        self.mul = MulticycleMul(width=max(self.mul_factor.width,\n                                           self.c_width))\n\n        super().__init__({\n            \"c\": In(Signature({\n                \"payload\": Out(signed(self.c_width)),\n                \"ready\": In(1),\n                \"valid\": Out(1)\n            })),\n            \"f\": Out(Signature({\n                \"payload\": Out(signed(self.f_width)),\n                \"ready\": In(1),\n                \"valid\": Out(1)\n            }))\n        })\n\n    def elaborate(self, plat):\n        m = Module()\n        m.submodules.mul = self.mul\n\n        m.d.comb += [\n            # res = 1.8*C\n            self.c.ready.eq(self.mul.inp.ready),\n            self.mul.inp.valid.eq(self.c.valid),\n            self.mul.inp.payload.a.eq(self.c.payload),\n            self.mul.inp.payload.b.eq(self.mul_factor),\n            self.mul.inp.payload.sign.eq(mul.Sign.SIGNED_UNSIGNED),\n\n            # F = res + 32, scaled to remove frac bits we don't need.\n            self.f.payload.eq((self.mul.outp.payload.o + self.add_factor) >>\n                              self.extra_bits),\n            self.f.valid.eq(self.mul.outp.valid),\n            self.mul.outp.ready.eq(self.f.ready)\n        ]\n\n        return m\n\n\ndef sim(*, c2f, start_c, end_c, gtkw=False):\n    sim = Simulator(c2f)\n    sim.add_clock(1e-6)\n\n    def proc():\n        yield c2f.f.ready.eq(1)\n        yield\n\n        for i in range(start_c, end_c):\n            yield c2f.c.payload.eq(i)\n            yield c2f.c.valid.eq(1)\n            yield\n            yield c2f.c.valid.eq(0)\n\n            # Wait for module to calculate results.\n            while (yield c2f.f.valid) != 1:\n                yield\n\n            # This is a low-effort attempt to print fixed-point numbers\n            # by converting them into floating point.\n            print((yield c2f.c.payload) / 2**c2f.qc[1],\n                  (yield c2f.f.payload) / 2**c2f.qf[1])\n\n    sim.add_sync_process(proc)\n\n    if gtkw:\n        with sim.write_vcd(\"c2f.vcd\", \"c2f.gtkw\"):\n            sim.run()\n    else:\n        sim.run()\n\n\nif __name__ == \"__main__\":\n    # See: https://en.wikipedia.org/wiki/Q_(number_format)\n    c2f = Celsius2Fahrenheit(qc=(8, 3), qf=(10, 3), scale_const=15)\n\n    if len(sys.argv) > 1 and sys.argv[1] == \"sim\":\n        if len(sys.argv) >= 2:\n            start_c = int(float(sys.argv[2]) * 2**c2f.qc[1])\n        else:\n            start_c = -2**(c2f.qc[0] + c2f.qc[1] - 1)\n\n        if len(sys.argv) >= 3:\n            end_c = int(float(sys.argv[3]) * 2**c2f.qc[1])\n        else:\n            end_c = 2**(c2f.qc[0] + c2f.qc[1] - 1)\n\n        sim(c2f=c2f, start_c=start_c, end_c=end_c, gtkw=True)\n    else:\n        print(convert(c2f))\n```\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Soft-core arithmetic components written in Amaranth HDL",
    "version": "0.1.1",
    "project_urls": {
        "Documentation": "https://smolarith.readthedocs.io",
        "Repository": "https://github.com/cr1901/smolarith"
    },
    "split_keywords": [
        "multiplication",
        "division",
        "hdl",
        "cpu"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "7f7490acb73720319372b951bce5292aeae7af8e7aac79f733af68fd0532c2ce",
                "md5": "83eb828e8677602f9fa1be29441a1f48",
                "sha256": "51b6087ab6f32cfc7b8e29ae57725901cd3e37bd997f3aa95106c61c7731f1a0"
            },
            "downloads": -1,
            "filename": "smolarith-0.1.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "83eb828e8677602f9fa1be29441a1f48",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 16584,
            "upload_time": "2024-03-06T04:10:20",
            "upload_time_iso_8601": "2024-03-06T04:10:20.639957Z",
            "url": "https://files.pythonhosted.org/packages/7f/74/90acb73720319372b951bce5292aeae7af8e7aac79f733af68fd0532c2ce/smolarith-0.1.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "c9b23677dc4a267a0992bb36ccf399552d53cf361f2efeb184166dbf598326ac",
                "md5": "f9529b169fa354268e5fe8a2285692a8",
                "sha256": "5fbe83144d96ad8ba913e368cbec54ecf09dbffe7de81fcd794a323a6961ba1c"
            },
            "downloads": -1,
            "filename": "smolarith-0.1.1.tar.gz",
            "has_sig": false,
            "md5_digest": "f9529b169fa354268e5fe8a2285692a8",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 20047,
            "upload_time": "2024-03-06T04:10:22",
            "upload_time_iso_8601": "2024-03-06T04:10:22.649772Z",
            "url": "https://files.pythonhosted.org/packages/c9/b2/3677dc4a267a0992bb36ccf399552d53cf361f2efeb184166dbf598326ac/smolarith-0.1.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-03-06 04:10:22",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "cr1901",
    "github_project": "smolarith",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "smolarith"
}
        
Elapsed time: 0.25503s