Python で FPGA/照度センサを使う

# PmodALS(照度センサ) を使う
iCE40UP はシステム・バス(SB) をもっており、そこを通してSPI コントローラにアクセスします。バスはアドレス信号線とデータ信号線といくつかの補助信号線を持ちます。ここではシステム・バスをPython でコントロールすることになります。システム全体は図に示される形になります。Polyphony で作るモジュールは入出力としてシステム・バスへのIF を持ちます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/99921/c099f137-24b0-e7c6-bd8a-551b4a0a2abf.png)

## いやいやシステム・バスと言われても、、、
と急に難しくなったのでもっと簡単に考えましょう。 PmodALS を単純に Lattice のボードに接続して SPI 経由でセンサーからのデータを持ってくることが出来るとしましょう。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/99921/85e92400-73ff-70d2-8bf7-21b774f3f76f.png)

## 難しそうな SPI 読出し関数は既にあり〼
SPI も難しそうですが既にあるとしましょう。worker を使うのでちょっと難しくなりますが、ソースの全容を掲げます。これを使えば簡単に SPI に繋がります。

```python:system_bus.py
import polyphony
from polyphony import testbench, module, is_worker_running
from polyphony.io import Port, Queue
from polyphony.typing import bit, uint8, uint3, uint4
from polyphony.timing import clksleep, clkfence, wait_rising, wait_falling, wait_value

CONVST_PULSE_CYCLE = 10
CONVERSION_CYCLE = 39

@polyphony.module
class system_bus:
    def __init__(self):
        self.stb = Port(bit, 'out', init=0)
        self.rw = Port(bit, 'out')
        self.addr = Port(uint8, 'out')
        self.data_out = Port(uint8, 'out')
        self.data_in = Port(uint8, 'in')
        self.ack = Port(bit, 'in')

        self.sbus_reset = Port(bit, 'out', init=0)
        self.sbus_ipload = Port(bit, 'out', init=0)
        self.sbus_ipdone = Port(bit, 'in')

        self.led = Port(bit, 'out')
        self.debug = Port(uint3, 'out')

        self.append_worker(self.worker)

    def write_data(self, addr:uint8, data:uint8):
        self.rw(1)
        self.addr(addr)
        self.data_out(data)
        clkfence()
        self.stb(1)
        clkfence()
        wait_value(1, self.ack)
        clkfence()
        self.stb(0)

    def read_data(self, addr:uint8):
        data:uint8 = 0
        self.rw(0)
        self.addr(addr)
        clkfence()
        self.stb(1)
        clkfence()
        wait_value(1, self.ack)
        data = self.data_in.rd()
        clkfence()
        self.stb(0)
        return data

    def read_spi_data16(self):
        debug_v:uint3 = 0

        debug_v = 4
        #self.debug.wr(4)

        self.write_data(0x06, 0x18)
        self.write_data(0x0A, 0xC0)
        self.write_data(0x0D, 0xFF)

        while True:
            irq_status = self.read_data(0x06)
            irq_trdy = (irq_status >> 4) & 1
            if irq_trdy == 1 :
                self.write_data(0x06, 0x10)
                break
        self.write_data(0x0D, 0xFF)
        #self.debug.wr(5)

        debug_v = 1

        while True:
            irq_status = self.read_data(0x06)
            irq_rrdy = (irq_status >> 3) & 1
            debug_v = 7 ^ debug_v
        #    self.debug.wr(debug_v)
            if irq_rrdy == 1:
                self.write_data(0x06, 0x08)
                break
        data0 = self.read_data(0x0E) << 8
        debug_v = 2
        #self.debug.wr(2)

        while True:
            irq_status = self.read_data(0x06)
            irq_rrdy = (irq_status >> 3) & 1
            if irq_rrdy == 1:
                debug_v = 4 ^ debug_v
                #self.debug.wr(debug_v)
                self.write_data(0x06, 0x08)
                break
        data1 = self.read_data(0x0E)
        #self.debug.wr(1)

        self.write_data(0x0A, 0x80)
        return (data0 | data1)

    def worker(self):

        self.sbus_reset.wr(1) 
        clksleep(8)

        self.sbus_reset.wr(0) 
        self.sbus_ipload(1)
        
        self.debug.wr(4)
        self._wait()
        wait_value(1, self.sbus_ipdone)
        self.sbus_ipload(0)
        self.debug.wr(0)

        clksleep(8)

        self.write_data(0x07, 0x18)
        self.write_data(0x0F, 2)
        self.write_data(0x09, 0x80)
        self.write_data(0x0A, 0xC0)
        self.write_data(0x0B, 0x02)

        self.write_data(0x0A, 0xC0)
        self.write_data(0x0D, 0x35)
        self.debug.wr(4)

        irq_status = 0
        while True:
            irq_status = self.read_data(0x06)
            irq_trdy = (irq_status >> 4) & 1
            if irq_trdy == 1 :
                self.write_data(0x06, 0x10)
                break

        #self.debug.wr((irq_status >> 3) & 3)

        while True:
            irq_status = self.read_data(0x06)
            irq_rrdy = (irq_status >> 3) & 1
            if irq_rrdy == 1 :
                self.write_data(0x06, irq_status)
                break

#        self.write_data(0x0D, 0x5A)
#        self.debug.wr((irq_status >> 3) & 3)
#        #self.debug.wr(1)
#        
#        while True:
#            irq_status = self.read_data(0x06)
#            irq_trdy = (irq_status >> 4) & 1
#            if irq_trdy == 1 :
#                self.write_data(0x06, 0x10)
#                #break
#            irq_rrdy = (irq_status >> 3) & 1
#            if irq_rrdy == 1 :
#                self.write_data(0x06, 0x08)
#                break
#
#        self.debug.wr(1)
        #self._wait()

        #while True:
        #    irq_status = self.read_data(0x06)
        #    if irq_status == 0 :
        #        break
        #    self._wait()
        #    self.write_data(0x06, irq_status)
        #    self._wait()

        #self.debug.wr((irq_status >> 3) & 2)
        #self._wait()
        self.write_data(0x0A, 0x80)

        #----------------------------------------------------
        self.write_data(0x0A, 0xC0)
        self.write_data(0x0D, 0x12)
        self.debug.wr(7)

        irq_status = 0
        while True:
            irq_status = self.read_data(0x06)
            irq_trdy = (irq_status >> 4) & 1
            if irq_trdy == 1 :
                self.write_data(0x0D, 0xAA)
                self.write_data(0x06, 0x10)
                break

        #self.debug.wr((irq_status >> 3) & 2)

        while True:
            irq_status = self.read_data(0x06)
            irq_rrdy = (irq_status >> 3) & 1
            if irq_rrdy == 1 :
                self.write_data(0x06, 0x08)
                break

        #self.debug.wr((irq_status >> 3) & 2)
        
        while True:
            irq_status = self.read_data(0x06)
            irq_rrdy = (irq_status >> 3) & 1
            if irq_rrdy == 1 :
                self.write_data(0x06, 0x08)
                break

        self.write_data(0x0A, 0x80)

        clkfence()
        clksleep(10)

        #self.debug.wr(4)
        while polyphony.is_worker_running():
            data16 = self.read_spi_data16()
            print(data16)
            self._wait()

        #while is_worker_running():
        #    debug_v = 7 ^ debug_v
        #    self.led(1)
        #    self._wait()
        #    self.led(0)
        #    self._wait()

    def _wait(self):
        INTERVAL=20000000
        for i in range(INTERVAL):
            pass

    def debug_out(self, debug_v:uint8):
        self.debug.wr(3)
        self._wait()

        for i in range(4):
            debug_v0 = (debug_v >> ((3 - i) * 2)) & 0x3 
            if ((i & 1) == 0) :
                debug_v0 |= 4
            self.debug.wr(debug_v0)
            self._wait()

        self.debug.wr(7)
        self._wait()
        self.debug.wr(0)
        self._wait()

    def rgb_out(self, n, v0:uint3, v1:uint3):
        self.debug.wr(v0)
        self._wait()
        self.debug.wr(v1)
        self._wait()

    def debug_out0(self, debug_v:uint8):
        self.rgb_out(20, 7, 0)
        for i in range(4):
            debug_v0 = (debug_v >> i) & 0x3
            debug_v1 = debug_v0
            if ((i & 1) == 0):
                debug_v1 |= 4
            self.rgb_out(10, debug_v0, debug_v1)
        self.rgb_out(30, 7, 5)
        self.debug.wr(0)
        
@polyphony.testbench
def test(sbus):
#    sbus.ack(0)
#    for i in range(5):
#        wait_rising(sbus.stb)
#        clksleep(1)
#        sbus.ack(1)
#        clkfence()
#        sbus.ack(0)
    
    clksleep(10)

if __name__ == '__main__':
    sbus = system_bus()
    test(sbus)


```

### Python でシミュレーション
Python でテストベンチを動かすことが出来ます。

```text:Pythonで実行
> python pmod_als.py
data0: 10912
```

### iverilog でシミュレーション

```text:simu.pyを実行
> <polyphony のディレクトリ>/simu.py pmod_als.py
Compiling: [=========================] 100% ... printresouces
(73, 68, 16)
data0: 10920
 1190:finish
```