Git Product home page Git Product logo

ariths-gen's Introduction

ArithsGen โ€“ tool for arithmetic circuits generation

made-with-python Documentation arXiv

Description

ArithsGen presents an open source tool that enables generation of various arithmetic circuits along with the possibility to export them to various formats which all serve their specific purpose. C language for easy simulation, Verilog for logic synthesis, BLIF for formal verification possibilities and CGP to enable further global optimization.

In contrast to standard HDL languages Python supports

  • Multiple output formats (BLIF, Verilog, C, Integer netlist)
  • Advanced language construction (better configuration, inheritance, etc.)
  • Support of various PDKs (for using library cells as half-adders and full-adders)

Reference

When you use this tool in your work/research, please cite the following article: KLHUFEK Jan and MRAZEK Vojtech. ArithsGen: Arithmetics Circuit Generator for HW Accelerators. In: 2022 25th International Symposium on Design and Diagnostics of Electronic Circuits and Systems (DDECS '22). Prague, 2022, p. 4.

@INPROCEEDINGS{klhufek:DDECS22,
   author = "Jan Klhufek and Vojtech Mrazek",
   title = "ArithsGen: Arithmetics Circuit Generator for HW Accelerators",
   pages = 4,
   booktitle = "2022 25th International Symposium on Design and Diagnostics of Electronic Circuits and Systems (DDECS '22)",
   year = 2022,
   location = "Prague, CZ"
}

Prebuild circuits

To enable fast work with the circuits, we published pre-build arithmetic circuits in various formats in generated_circuits folder and as a Release.

Usage

python3 generate_test.py
cd test_circuits
ls

Example of generation

#Example generation of Verilog representation of 8-bit unsigned dadda multiplier that uses cla to provide the final product
a = Bus(N=8, prefix="a_bus")
b = Bus(N=8, prefix="b_bus")

u_dadda = UnsignedDaddaMultiplier(a=a, b=b, prefix="h_u_dadda_cla8", unsigned_adder_class_name=UnsignedCarryLookaheadAdder)
u_dadda.get_v_code_hier(open("h_u_dadda_cla8.v", "w"))

Simple arithmetic circuits

See Ripple Carry Adder file for a basic example.

Complex circuits

It is possible to combine some basic circuits to generate more complex circuits (such as MAC). The design can be parametrised (i.e., you can pass UnsignedArraymultiplier as an input parameter).

from ariths_gen.core.arithmetic_circuits.arithmetic_circuit import ArithmeticCircuit
from ariths_gen.core.arithmetic_circuits import GeneralCircuit
from ariths_gen.wire_components import Bus, Wire
from ariths_gen.multi_bit_circuits.adders import UnsignedRippleCarryAdder
from ariths_gen.multi_bit_circuits.multipliers import UnsignedArrayMultiplier, UnsignedDaddaMultiplier
import os

class MAC(GeneralCircuit):
    def __init__(self, a: Bus, b: Bus, r: Bus, prefix: str = "", name: str = "mac", **kwargs):
        super().__init__(prefix=prefix, name=name, out_N=2*a.N+1, inputs=[a, b, r], **kwargs)
        assert a.N == b.N
        assert r.N == 2 * a.N

        self.mul = self.add_component(UnsignedArrayMultiplier(a=a, b=b, prefix=self.prefix, name=f"u_arrmul{a.N}", inner_component=True))
        self.add = self.add_component(UnsignedRippleCarryAdder(a=r, b=self.mul.out, prefix=self.prefix, name=f"u_rca{r.N}", inner_component=True))
        self.out.connect_bus(connecting_bus=self.add.out)

# usage
if __name__ == "__main__":
    os.makedirs("test_circuits/mac", exist_ok=True)
    mymac = MAC(Bus("a", 8), Bus("b", 8), Bus("acc", 16))
    mymac.get_v_code_hier(open("test_circuits/mac/mac_hier.v", "w"))
    mymac.get_c_code_hier(open("test_circuits/mac/mac_hier.c", "w"))
    mymac.get_c_code_flat(open("test_circuits/mac/mac_flat.c", "w"))

Documentation

The automatically generated documentation is available at https://ehw-fit.github.io/ariths-gen/ .

Supporting various PDK kits

When one uses a specific process design kit (PDK), it is not effective to implement half- and full-adders using two-inputs logic gates. These circuits are directly implemented as CMOS modules and are more effective than heuristic optimization by synthesis tool. If you want to use for example FreePDK45 library, you can call a following function before verilog code generating.

from ariths_gen import set_pdk45_library
set_pdk45_library()

You can add a support of arbitrary PDK (see an example ).

Approximate circuits

Besides the accurate arithmetic circuits you can generate some approximate circuits. Currently we support Broken Array Multiplier and Truncated Multiplier both with fully connected architectures composed from half/full adders as well as faster implementations using carry save multiplier. For more details please follow files in folder approximate_multipliers. You can simply run

python3 generate_axmuls.py

to get the approximate circuits.

The module also supports evaluation of the proposed circuits. You can call the instation as a function (even with numpy-array input) to obtain the results of multiplication operation.

from ariths_gen.wire_components.buses import Bus
from ariths_gen.multi_bit_circuits.approximate_multipliers import UnsignedBrokenArrayMultiplier
a = Bus(N=8, prefix="a_bus")
b = Bus(N=8, prefix="b_bus")

# Create BAM 
bam = UnsignedBrokenArrayMultiplier(a, b, horizontal_cut=4, vertical_cut=4)

print("43 * 84 = ", bam(43, 84), " expected: ", 43 * 84)
# 43 * 84 =  3440  expected:  3612

for all of the possible combinations.

# Evaluate all using b'casting
import numpy as np
import matplotlib.pyplot as plt
va = np.arange(256).reshape(1, -1)
vb = va.reshape(-1, 1)
r = bam(va, vb)

cax = plt.imshow(np.abs(r - (va * vb)))
plt.colorbar(cax)
plt.title("Absolute difference")
plt.xlabel("a")
plt.ylabel("b")

print("Mean average error", np.abs(r - (va * vb)).mean())
# Mean average error 956.25

bam (v=4,h=4)

Formal verification

The yosys_equiv_check.sh script enables to formally check the equivalence of generated Verilog and BLIF representations of the same circuit. It uses the Yosys Open SYnthesis Suite tool by Claire Xenia Wolf. For further information, please visit: https://yosyshq.readthedocs.io/projects/yosys/en/latest/index.html.

Execute permission

chmod +x yosys_equiv_check.sh

Usage

./yosys_equiv_check.sh -v "verilog_file" -b "blif_file" -m "method" [-H]

For more detailed description of script's usage, use help.

./yosys_equiv_check.sh -h|--help

CGP testing

The chr2c.py script converts the input CGP chromosome generated by ArithsGen to the corresponding C code and prints it to standard output.

Usage

python3 chr2c.py input.chr > output.c

ariths-gen's People

Contributors

github-actions[bot] avatar honzastor avatar mrazekv avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

ariths-gen's Issues

Implementation of Python simulation

@honzastor please implement python simulation (compatible with NumPy) of the proposed circuit. It will be helpful for @AlenkaDrlickova bachelor work

Goal

a = Bus(N=3, prefix="a")
b = Bus(N=3, prefix="b")
circuit = UnsignedArrayMultiplier(a, b, name="arrmul")
print(circuit(3, 4))
print(circuit(np.arange(8), np.arange(8).reshape(-1, 1))) # numpy b'casting

Steps

  • Remove the file-closers in the generators
  • Implement a function get_python_code (we are interested in the flatted only). The code is similar as c-code without '{', '}', and ';' symbols; without type definitions and different syntax in the function prototype
  • Add a __call__ function

Proof of concept

import sys
import os
import numpy as np
import re
from io import StringIO

class circ:
    def __init__(self):
        self.pyc = None

    def get_python_code(self, filewriter):
        filewriter.write("""def u_wallace_cska3(a, b):
  u_wallace_cska3_out = 0

  u_wallace_cska3_and_2_0 = ((a >> 2) & 0x01) & ((b >> 0) & 0x01)
  u_wallace_cska3_and_1_1 = ((a >> 1) & 0x01) & ((b >> 1) & 0x01)
  u_wallace_cska3_ha0_xor0 = ((u_wallace_cska3_and_2_0 >> 0) & 0x01) ^ ((u_wallace_cska3_and_1_1 >> 0) & 0x01)
  u_wallace_cska3_ha0_and0 = ((u_wallace_cska3_and_2_0 >> 0) & 0x01) & ((u_wallace_cska3_and_1_1 >> 0) & 0x01)
  u_wallace_cska3_and_2_1 = ((a >> 2) & 0x01) & ((b >> 1) & 0x01)
  u_wallace_cska3_ha1_xor0 = ((u_wallace_cska3_ha0_and0 >> 0) & 0x01) ^ ((u_wallace_cska3_and_2_1 >> 0) & 0x01)
  u_wallace_cska3_ha1_and0 = ((u_wallace_cska3_ha0_and0 >> 0) & 0x01) & ((u_wallace_cska3_and_2_1 >> 0) & 0x01)
  u_wallace_cska3_and_0_0 = ((a >> 0) & 0x01) & ((b >> 0) & 0x01)
  u_wallace_cska3_and_1_0 = ((a >> 1) & 0x01) & ((b >> 0) & 0x01)
  u_wallace_cska3_and_0_2 = ((a >> 0) & 0x01) & ((b >> 2) & 0x01)
  u_wallace_cska3_and_1_2 = ((a >> 1) & 0x01) & ((b >> 2) & 0x01)
  u_wallace_cska3_and_0_1 = ((a >> 0) & 0x01) & ((b >> 1) & 0x01)
  u_wallace_cska3_and_2_2 = ((a >> 2) & 0x01) & ((b >> 2) & 0x01)
  u_wallace_cska3_u_cska4_xor0 = ((u_wallace_cska3_and_1_0 >> 0) & 0x01) ^ ((u_wallace_cska3_and_0_1 >> 0) & 0x01)
  u_wallace_cska3_u_cska4_ha0_xor0 = ((u_wallace_cska3_and_1_0 >> 0) & 0x01) ^ ((u_wallace_cska3_and_0_1 >> 0) & 0x01)
  u_wallace_cska3_u_cska4_ha0_and0 = ((u_wallace_cska3_and_1_0 >> 0) & 0x01) & ((u_wallace_cska3_and_0_1 >> 0) & 0x01)
  u_wallace_cska3_u_cska4_xor1 = ((u_wallace_cska3_and_0_2 >> 0) & 0x01) ^ ((u_wallace_cska3_ha0_xor0 >> 0) & 0x01)
  u_wallace_cska3_u_cska4_fa0_xor0 = ((u_wallace_cska3_and_0_2 >> 0) & 0x01) ^ ((u_wallace_cska3_ha0_xor0 >> 0) & 0x01)
  u_wallace_cska3_u_cska4_fa0_and0 = ((u_wallace_cska3_and_0_2 >> 0) & 0x01) & ((u_wallace_cska3_ha0_xor0 >> 0) & 0x01)
  u_wallace_cska3_u_cska4_fa0_xor1 = ((u_wallace_cska3_u_cska4_fa0_xor0 >> 0) & 0x01) ^ ((u_wallace_cska3_u_cska4_ha0_and0 >> 0) & 0x01)
  u_wallace_cska3_u_cska4_fa0_and1 = ((u_wallace_cska3_u_cska4_fa0_xor0 >> 0) & 0x01) & ((u_wallace_cska3_u_cska4_ha0_and0 >> 0) & 0x01)
  u_wallace_cska3_u_cska4_fa0_or0 = ((u_wallace_cska3_u_cska4_fa0_and0 >> 0) & 0x01) | ((u_wallace_cska3_u_cska4_fa0_and1 >> 0) & 0x01)
  u_wallace_cska3_u_cska4_xor2 = ((u_wallace_cska3_and_1_2 >> 0) & 0x01) ^ ((u_wallace_cska3_ha1_xor0 >> 0) & 0x01)
  u_wallace_cska3_u_cska4_fa1_xor0 = ((u_wallace_cska3_and_1_2 >> 0) & 0x01) ^ ((u_wallace_cska3_ha1_xor0 >> 0) & 0x01)
  u_wallace_cska3_u_cska4_fa1_and0 = ((u_wallace_cska3_and_1_2 >> 0) & 0x01) & ((u_wallace_cska3_ha1_xor0 >> 0) & 0x01)
  u_wallace_cska3_u_cska4_fa1_xor1 = ((u_wallace_cska3_u_cska4_fa1_xor0 >> 0) & 0x01) ^ ((u_wallace_cska3_u_cska4_fa0_or0 >> 0) & 0x01)
  u_wallace_cska3_u_cska4_fa1_and1 = ((u_wallace_cska3_u_cska4_fa1_xor0 >> 0) & 0x01) & ((u_wallace_cska3_u_cska4_fa0_or0 >> 0) & 0x01)
  u_wallace_cska3_u_cska4_fa1_or0 = ((u_wallace_cska3_u_cska4_fa1_and0 >> 0) & 0x01) | ((u_wallace_cska3_u_cska4_fa1_and1 >> 0) & 0x01)
  u_wallace_cska3_u_cska4_xor3 = ((u_wallace_cska3_ha1_and0 >> 0) & 0x01) ^ ((u_wallace_cska3_and_2_2 >> 0) & 0x01)
  u_wallace_cska3_u_cska4_fa2_xor0 = ((u_wallace_cska3_ha1_and0 >> 0) & 0x01) ^ ((u_wallace_cska3_and_2_2 >> 0) & 0x01)
  u_wallace_cska3_u_cska4_fa2_and0 = ((u_wallace_cska3_ha1_and0 >> 0) & 0x01) & ((u_wallace_cska3_and_2_2 >> 0) & 0x01)
  u_wallace_cska3_u_cska4_fa2_xor1 = ((u_wallace_cska3_u_cska4_fa2_xor0 >> 0) & 0x01) ^ ((u_wallace_cska3_u_cska4_fa1_or0 >> 0) & 0x01)
  u_wallace_cska3_u_cska4_fa2_and1 = ((u_wallace_cska3_u_cska4_fa2_xor0 >> 0) & 0x01) & ((u_wallace_cska3_u_cska4_fa1_or0 >> 0) & 0x01)
  u_wallace_cska3_u_cska4_fa2_or0 = ((u_wallace_cska3_u_cska4_fa2_and0 >> 0) & 0x01) | ((u_wallace_cska3_u_cska4_fa2_and1 >> 0) & 0x01)
  u_wallace_cska3_u_cska4_and_propagate00 = ((u_wallace_cska3_u_cska4_xor0 >> 0) & 0x01) & ((u_wallace_cska3_u_cska4_xor2 >> 0) & 0x01)
  u_wallace_cska3_u_cska4_and_propagate01 = ((u_wallace_cska3_u_cska4_xor1 >> 0) & 0x01) & ((u_wallace_cska3_u_cska4_xor3 >> 0) & 0x01)
  u_wallace_cska3_u_cska4_and_propagate02 = ((u_wallace_cska3_u_cska4_and_propagate00 >> 0) & 0x01) & ((u_wallace_cska3_u_cska4_and_propagate01 >> 0) & 0x01)
  u_wallace_cska3_u_cska4_mux2to10_not0 = ~(((u_wallace_cska3_u_cska4_and_propagate02 >> 0) & 0x01)) & 0x01
  u_wallace_cska3_u_cska4_mux2to10_and1 = ((u_wallace_cska3_u_cska4_fa2_or0 >> 0) & 0x01) & ((u_wallace_cska3_u_cska4_mux2to10_not0 >> 0) & 0x01)

  u_wallace_cska3_out |= ((u_wallace_cska3_and_0_0 >> 0) & 0x01) << 0
  u_wallace_cska3_out |= ((u_wallace_cska3_u_cska4_ha0_xor0 >> 0) & 0x01) << 1
  u_wallace_cska3_out |= ((u_wallace_cska3_u_cska4_fa0_xor1 >> 0) & 0x01) << 2
  u_wallace_cska3_out |= ((u_wallace_cska3_u_cska4_fa1_xor1 >> 0) & 0x01) << 3
  u_wallace_cska3_out |= ((u_wallace_cska3_u_cska4_fa2_xor1 >> 0) & 0x01) << 4
  u_wallace_cska3_out |= ((u_wallace_cska3_u_cska4_mux2to10_and1 >> 0) & 0x01) << 5
  return u_wallace_cska3_out""")

    def __call__(self, *args):
        if not self.pyc:
            buf = StringIO()
            self.get_python_code(buf)

            globs, locs = {}, {}
            exec(buf.getvalue(), globs, locs)
            #print(locs)
            self.pyc = locs["u_wallace_cska3"]

        return self.pyc(*args)
        

c = circ()
print(c(1, 2))
# 2
print(c(3, 4))
# 12
print(c(np.arange(8), np.arange(8).reshape(-1, 1)))
#[[ 0  0  0  0  0  0  0  0]
# [ 0  1  2  3  4  5  6  7]
# [ 0  2  4  6  8 10 12 14]
# [ 0  3  6  9 12 15 18 21]
# [ 0  4  8 12 16 20 24 28]
# [ 0  5 10 15 20 25 30 35]
# [ 0  6 12 18 24 30 36 42]
# [ 0  7 14 21 28 35 42 49]]

CGP code as input

With this update, we are able to generate component (circuit) from arbitrary CGP code

Example:

        mul = c(a, b)
        code = StringIO()
        mul.get_cgp_code_flat(code)
        cgp_code = code.getvalue() # getting CGP code

        # must specify the inputs 
        mul2 = UnsignedCGPCircuit(cgp_code, [N, N])
        r = mul2(av, bv)

I need this extension for testing / synthesizing the results from CGP.

TODOs

  • Implementation @mrazekv
  • Testing (signed and unsigned circuits) @mrazekv
  • Merging to devel #11
  • Documentation / file structure / cleaning @honzastor

This extension also might help @AlenkaDrlickova with her thesis.

Sign extension

The extension is necessary, however, there is some bug in the implementation.
Please check commit ecac55a and if possible, include this functionality to bus_extend function

multi driver issue in s_csamul_*.v

Hi,

I found that 24 generated signed csamul circuits (in generated_circuits/verilog_circuits/hier/s_csamul_*.v) has multi driver issue.

for example,

nand_gate nand_gate_s_csamul_cla12_nand11_1(.a(a[11]), .b(b[1]), .out(s_csamul_cla12_nand11_1));
ha ha_s_csamul_cla12_ha11_1_out(.a(s_csamul_cla12_nand11_1[0]), .b(1'b1), .ha_xor0(s_csamul_cla12_ha11_1_xor0), .ha_and0(s_csamul_cla12_nand11_1));

Support of constant wires in mux

from ariths_gen.core.arithmetic_circuits.arithmetic_circuit import ArithmeticCircuit
from ariths_gen.core.arithmetic_circuits import GeneralCircuit
from ariths_gen.wire_components import Bus, Wire, ConstantWireValue0
from ariths_gen.one_bit_circuits.one_bit_components.three_input_one_bit_components import TwoOneMultiplexer
from ariths_gen.multi_bit_circuits.adders import UnsignedRippleCarryAdder
from ariths_gen.multi_bit_circuits.approximate_adders import QuAdder
from ariths_gen.multi_bit_circuits.multipliers import UnsignedArrayMultiplier, UnsignedDaddaMultiplier
from ariths_gen.pdk import set_pdk45_library
import os, sys


class MAC(GeneralCircuit):
    def __init__(self, a, prefix, name):
        super().__init__(prefix=prefix, name=name, inputs=[a], out_N=2)

        c1 = ConstantWireValue0()


        b = self.add_component(TwoOneMultiplexer(a[0], a[1], a[2], prefix=f"{prefix}_m1"))
        b2 = self.add_component(TwoOneMultiplexer(a[0], c1, a[2], prefix=f"{prefix}_m1a"))
        c = self.add_component(TwoOneMultiplexer(c1, c1, c1, prefix=f"{prefix}_m2"))
        self.out[0] = b.out[0]
        self.out[1] = c.out[0]

set_pdk45_library()
m = MAC(Bus("a", 3), "mac", "mac")

m.get_v_code_flat(file_object=sys.stdout)

should produce

module mac_mac(input [2:0] a, output [1:0] mac_mac_out);
  wire mac_m1_and0;
  wire mac_m1_not0;
  wire mac_m1_and1;
  wire mac_m1_xor0;
  wire mac_m1a_not0;
  wire mac_m1a_and1;

  wire neg_mac_m1_xor0;
  MUX2X1 mac_m1 (.A(a[0]), .B(a[1]), .S(a[2]), .Y(neg_mac_m1_xor0));
  assign mac_m1_xor0 = ~neg_mac_m1_xor0;
  wire neg_mac_m1a_and1;
  MUX2X1 mac_m1a (.A(a[0]), .B(1'b0), .S(a[2]), .Y(neg_mac_m1a_and1)); // no constant_wire_0
  assign mac_m1a_and1 = ~neg_mac_m1a_and1;

  assign mac_mac_out[0] = mac_m1_xor0;
  assign mac_mac_out[1] = 1'b0;
endmodule

Test of existing prefixes

Test code:

from ariths_gen.wire_components import Wire, Bus

import sys
from ariths_gen.one_bit_circuits.logic_gates import AndGate
from ariths_gen.one_bit_circuits.one_bit_components import TwoOneMultiplexer
from ariths_gen.core.arithmetic_circuits import GeneralCircuit

class test_circuit(GeneralCircuit):
    def __init__(self, a: Bus, prefix="test_circuit", **kwargs):
        super().__init__(prefix=prefix, name="test_circuit", inputs=[a], out_N=1, **kwargs)
        g = self.add_component(AndGate(a[0], a[1], prefix="g2"))
        g2 = self.add_component(AndGate(g.out, a[2], prefix="g2"))
        g3 = self.add_component(AndGate(g2.out, g.out, prefix="g2"))
        self.out[0] = g3.out

circ = test_circuit(Bus("a", 3), "c1")
circ.get_v_code_flat(file_object=sys.stdout)

produces invalid verilog code

module c1_test_circuit(input [2:0] a, output [0:0] c1_test_circuit_out);
  wire g2;
  wire g2;
  wire g2;

  assign g2 = a[0] & a[1];
  assign g2 = g2 & a[2];
  assign g2 = g2 & g2;

  assign c1_test_circuit_out[0] = g2;
endmodule

Proposed solution:

  • during adding (or generating?) check, if the prefix is unique. If not, raise an exception KeyError
  • write a small test
import pytest
def test_unique():
    from ariths_gen.wire_components import Wire, Bus

    import sys
    from ariths_gen.one_bit_circuits.logic_gates import AndGate
    from ariths_gen.one_bit_circuits.one_bit_components import TwoOneMultiplexer
    from ariths_gen.core.arithmetic_circuits import GeneralCircuit

    class test_circuit(GeneralCircuit):
        def __init__(self, a: Bus, prefix="test_circuit", **kwargs):
            super().__init__(prefix=prefix, name="test_circuit", inputs=[a], out_N=1, **kwargs)
            g = self.add_component(AndGate(a[0], a[1], prefix="g2"))
            g2 = self.add_component(AndGate(g.out, a[2], prefix="g2"))
            g3 = self.add_component(AndGate(g2.out, g.out, prefix="g2"))
            self.out[0] = g3.out

    with pytest.raises(KeyError):
        circ = test_circuit(Bus("a", 3), "c1")
        circ.get_v_code_flat(file_object=sys.stdout)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.