// Copy from top.scala

// And change the interface

package final_project

import chisel3._
import chisel3.util._

import chisel3.experimental.BundleLiterals._

class FinalTop extends Module{
    val io = IO(new Bundle{
        val start = Input(Bool())
        val input = Input(SInt(16.W))
        val valid_in = Input(Bool())
        val complete = Output(Bool())
        val complete_output = Output(Bool())
        val valid = Output(Bool())
        val output = Output(SInt(16.W))
    })

    val reader1 = Module(new GraphReader(StdPara.addr_w, StdPara.h_w, StdPara.c_w, StdPara.id_w, StdPara.big_w)).io
    val reader2 = Module(new GraphReader(StdPara.addr_w, StdPara.h_w, StdPara.c_w, StdPara.id_w, StdPara.big_w)).io
    val read_pack = Module(new PackReadData(StdPara.w, StdPara.h_w, StdPara.c_w, StdPara.big_w)).io
    val read_switch = Module(new ReadSwitch(StdPara.w, StdPara.h_w, StdPara.c_w, StdPara.para_num)).io
    val read_weight = Module(new WeightReader(StdPara.w, StdPara.weight_addr_w)).io
    val calc8x8 = Module(new Calc8x8(StdPara.w, StdPara.para_num)).io
    val accu = Module(new Accumu(StdPara.w, StdPara.bias_addr_w, StdPara.bias_w, StdPara.para_num)).io

    val cache_writer = Module(new CacheWriter(StdPara.w, StdPara.para_num)).io
    val quant = Module(new Quant(StdPara.w)).io
    val write_switch = Module(new WriteSwitch(StdPara.w, 3)).io
    val writer = Module(new RealWriter(StdPara.w, StdPara.addr_w, StdPara.h_w, StdPara.c_w, StdPara.id_w)).io
    val maxpool = Module(new Maxpool(StdPara.w)).io

    val rom_bias = if(StdPara.para_num==4) Module(new ROMBias(StdPara.para_num)).io else Module(new ROMHalfBias(StdPara.para_num)).io
    // val rom_weight = Module(new ROMWeight(StdPara.para_num)).io
    // val rom_bias = Module(new ROMBias(StdPara.para_num)).io
    val bram = Module(new RAMGroup(StdPara.w, StdPara.addr_w, StdPara.id_w)).io

    io.complete := false.B
    io.valid := false.B
    io.output := 0.S
    bram.rd_addr1 := reader1.to_banks
    reader1.flag_job := false.B
    reader1.valid_in := false.B
    reader1.job := 0.U.asTypeOf(reader1.job)
    reader1.job_type := 0.U.asTypeOf(reader1.job_type)
    reader1.job2 := 0.U.asTypeOf(reader1.job2)
    reader1.signal := false.B

    bram.rd_valid_in := reader2.valid_out
    bram.rd_addr2 := reader2.to_banks
    reader2.flag_job := false.B
    reader2.valid_in := false.B
    reader2.job := 0.U.asTypeOf(reader2.job)
    reader2.job_type := 0.U.asTypeOf(reader2.job_type)
    reader2.job2 := 0.U.asTypeOf(reader2.job2)
    reader2.signal := false.B

    read_weight.valid_in := false.B
    read_weight.addr_end := 0.U
    read_weight.begin_addr := 0.U
    rom_weight.addr := read_weight.addr
    read_weight.flag_job := false.B

    read_pack.valid_in := bram.rd_valid_out
    read_pack.flag_job := false.B
    read_pack.job := 0.U.asTypeOf(read_pack.job)
    read_pack.from_big := bram.rd_big
    read_pack.from_small := bram.rd_small

    read_switch.flag_job := false.B
    read_switch.job := 0.U.asTypeOf(read_switch.job)
    read_switch.valid_in := read_pack.valid_out
    read_switch.from := read_pack.output
    read_switch.from_weight := rom_weight.out
    read_switch.in_h := 0.U
    read_switch.in_chan := 0.U
    read_switch.valid_in_from_user := false.B
    read_switch.data_from_user := 0.U.asTypeOf(read_switch.data_from_user)

    calc8x8.input := read_switch.to_calc8x8
    calc8x8.flag := 0.U.asTypeOf(calc8x8.flag)
    calc8x8.weight := read_switch.to_weight
    calc8x8.valid_in := read_switch.valid_out_calc8x8
    calc8x8.mask := 0.U
    calc8x8.multmap := read_switch.to_multmap

    accu.in_from_calc8x8 := calc8x8.output
    accu.valid_in := calc8x8.valid_out
    accu.flag_job := false.B
    accu.csum := 0.U
    accu.bias_begin_addr := 0.U
    accu.bias_end_addr := 0.U
    rom_bias.addr := accu.bias_addr
    accu.bias_in := rom_bias.out
    accu.is_in_use := false.B

    cache_writer.in_from_accumu := accu.result
    cache_writer.valid_in := accu.valid_out

    quant.in_from_accumu := cache_writer.out
    quant.valid_in := cache_writer.valid_out
    quant.flag_job := false.B
    quant.quant_in := 0.U.asTypeOf(quant.quant_in)

    maxpool.from_big := bram.rd_big
    for(t <- 0 to 1){
        maxpool.from_small(t)(0) := bram.rd_small(t)(0)
        maxpool.from_small(t)(1) := bram.rd_small(t)(1)
    }

    maxpool.valid_in := read_switch.valid_out_maxp

    //write_switch.valid_in(1) := false.B
    //write_switch.input(1) := 0.U.asTypeOf(write_switch.input(1))

    write_switch.valid_in(0) := quant.valid_out // conv
    write_switch.valid_in(1) := maxpool.valid_out // maxpool
    write_switch.valid_in(2) := read_switch.valid_out_copy // go through

    write_switch.input(0) := quant.result
    write_switch.input(1) := maxpool.result
    write_switch.input(2) := read_switch.to_copy

    writer.in_from_quant := write_switch.output
    writer.valid_in := write_switch.valid_out
    writer.flag_job := false.B
    writer.job := 0.U.asTypeOf(writer.job)

    // io.output_ls := quant.result.mat(0)

    /*dontTouch(writer.to_banks)
    dontTouch(writer.to_bigbank)
    dontTouch(writer.to_smallbank)
    dontTouch(writer.valid_out)*/
    bram.wr_addr := writer.to_banks
    bram.to_bigbank := writer.to_bigbank
    bram.to_smallbank := writer.to_smallbank
    bram.wr_valid_in := writer.valid_out

    def nxt_big(x: Int): Int = {
        val L = StdPara.big_min_addr
        val R = StdPara.big_max_addr
        return (x-L)%(R-L+1)+L
    }
    def nxt_small(x: Int): Int = {
        val L = StdPara.small_min_addr
        val R = StdPara.small_max_addr
        return (x-L)%(R-L+1)+L
    }

    var big_addr = StdPara.big_min_addr
    var small_addr = StdPara.small_min_addr
    var weight_addr = 0
    var bias_addr = 0

    val counter = RegInit(0.U.asTypeOf(ACounter(16.W)))
    val state = RegInit(0.U(6.W))

    var num_stages = 0

    io.complete_output := false.B

    WAIT_START()
    CONV(4, 4, 4, 16, 30, 15)
    // LAST_PRINT(8, 8, 4)
    // NEXT_LOOP()
    // LAST_PRINT(8, 8, 4)
    // END()
    // WRITEHALF_CONV1(8, 8, 17, 8)
    // LAST_PRINT(4, 4, 32)

    // RELU(4, 4, 16, 15+15, 17)
    // CONV(4, 4, 16, 32, 32, 16)
    // LAST_PRINT(4, 4, 16)
    RELU0(4, 4, 16, 15+15, 17)
    // weight_addr = 0
    // bias_addr = 0
    // big_addr = 0
    // small_addr = 0
    // CONV(4, 4, 4, 16, 30, 15)
    // RELU(4, 4, 16, 15+15, 17)
    // LAST_PRINT(4, 4, 16)

    CONV1(4, 4, 16, 32, 32, 16)
    RELU1(4, 4, 32, 16+15, 17)
    MAXPOOL(4, 4, 32)
    //conv_2
    CONV(2, 2, 32, 64, 32, 14)
    RELU(2, 2, 64, 14+15, 14)

    // conv_6
    CONV(2, 2, 64, 64, 29, 11)
    RELU(2, 2, 64, 11+15, 11)

    // conv_u1
    UPS(2, 2, 64)

    CONV(4, 4, 64, 32, 26, 8)
    RELU(4, 4, 32, 8+15, 8)
    // LAST_PRINT(4, 4, 32)
    println(num_stages)
    CONVMULT()
    println(num_stages)

    // conv_u4
    UPS(4, 4, 16)
    CONV(8, 8, 16, 16, 27, 12)
    RELU(8, 8, 16, 12+15, 12)

    // conv_10
    CONV(8, 8, 16, 4, 29, 13)
    LAST_PRINT(8, 8, 4)
    NEXT_LOOP()

    // val checksum = RegInit(0.U(16.W))
    // CHECK_PRINT()
    // LAST_CHECK(8, 8, 4)
    // LAST_PRINT(2, 2, 32)

    def WAIT_START(){
        when(state===(num_stages).U){
            when(io.start){
                state := (num_stages+1).U
            }
        }
        num_stages += 1
    }
    def CONVMULT(){
        WRITEHALF_CONV1(8, 8, 17, 8)
        println(num_stages)
        CONV(4, 4, 64, 16, 22, 6)
        println(num_stages)
        RELU(4, 4, 16, 6+15, 6)
        println(num_stages)
        MULT(6+17, 11)
        // LAST_PRINT(4, 4, 16)
    }

    def MULT(q_in: Int, q_out: Int){
        val paras = GenAllPara(addr_w=StdPara.addr_w, h_w=StdPara.h_w, c_w=StdPara.c_w, id_w=StdPara.id_w, big_w=StdPara.big_w,
            width=4,
            height=4,
            wr_width=4,
            wr_height=4,
            in_chan=16,
            out_chan=16,
            para_num=StdPara.para_num,
            mask=StdPara.mask,
            big_begin_addr=big_addr,
            big_max_addr=StdPara.big_max_addr,
            big_min_addr=StdPara.big_min_addr,
            wr_big_begin_addr=nxt_big(big_addr+16*4*4/2),
            wr_big_max_addr=StdPara.big_max_addr,
            wr_big_min_addr=StdPara.big_min_addr,
            small_begin_addr=small_addr,
            small_max_addr=StdPara.small_max_addr,
            small_min_addr=StdPara.small_min_addr,
            wr_small_begin_addr=nxt_small(small_addr+16*4*4/4),
            wr_small_max_addr=StdPara.small_max_addr,
            wr_small_min_addr=StdPara.small_min_addr,
            weight_begin_addr=0,
            bias_begin_addr=0,
            quant_q_in=q_in,
            quant_q_out=q_out
        )

        val paras2 = GenAllPara(addr_w=StdPara.addr_w, h_w=StdPara.h_w, c_w=StdPara.c_w, id_w=StdPara.id_w, big_w=StdPara.big_w,
            width=4,
            height=4,
            wr_width=4,
            wr_height=4,
            in_chan=16,
            out_chan=16,
            para_num=StdPara.para_num,
            mask=StdPara.mask,
            big_begin_addr=StdPara.big_conv0_addr,
            big_max_addr=StdPara.big_global_addr,
            big_min_addr=0,
            wr_big_begin_addr=0,
            wr_big_max_addr=0,
            wr_big_min_addr=0,
            small_begin_addr=StdPara.small_conv0_addr,
            small_max_addr=StdPara.small_global_addr,
            small_min_addr=0,
            wr_small_begin_addr=0,
            wr_small_max_addr=0,
            wr_small_min_addr=0,
            weight_begin_addr=0,
            bias_begin_addr=0,
            quant_q_in=0,
            quant_q_out=0
        )

        big_addr = nxt_big(big_addr+16*4*4/2)
        small_addr = nxt_small(small_addr+16*4*4/4)
        when(state===(num_stages).U){
            paras.set_copy_reader1(reader1, 1)
            paras.set_copy_reader2(reader2, 1)
            paras2.set_copy_reader1_job2(reader1, 1)
            paras2.set_copy_reader2_job2(reader2, 1)
            paras.set_ups_read_pack(read_pack)
            paras.set_mult_switch(read_switch)
            paras.set_unuse_accu(accu)
            paras.set_quant(quant)
            paras.set_write(writer)
            counter.set((16*4*4-1).U)
            state := (num_stages+1).U
        }
        num_stages += 1
        when(state===(num_stages).U){
            when(writer.valid_out){
                when(counter.inc()){
                    state := (num_stages+1).U
                }
            }
            reader1.valid_in := true.B
            reader2.valid_in := true.B
            reader1.signal := true.B
            reader2.signal := true.B
            calc8x8.flag := CalcType.calcMult
        }
        num_stages += 1

    }

    def WRITEHALF_CONV1(q_in: Int, q_out: Int, q_in2: Int, q_out2: Int){
        val in_chan = 32
        val paras = GenAllPara(addr_w=StdPara.addr_w, h_w=StdPara.h_w, c_w=StdPara.c_w, id_w=StdPara.id_w, big_w=StdPara.big_w,
            width=4,
            height=4,
            wr_width=4,
            wr_height=4,
            in_chan=in_chan,
            out_chan=in_chan*2,
            para_num=StdPara.para_num,
            mask=StdPara.mask,
            big_begin_addr=big_addr,
            big_max_addr=StdPara.big_max_addr,
            big_min_addr=StdPara.big_min_addr,
            wr_big_begin_addr=nxt_big(big_addr+in_chan*4*4/2+0),
            wr_big_max_addr=StdPara.big_max_addr,
            wr_big_min_addr=StdPara.big_min_addr,
            small_begin_addr=small_addr,
            small_max_addr=StdPara.small_max_addr,
            small_min_addr=StdPara.small_min_addr,
            wr_small_begin_addr=nxt_small(small_addr+in_chan*4*4/4+0),
            wr_small_max_addr=StdPara.small_max_addr,
            wr_small_min_addr=StdPara.small_min_addr,
            weight_begin_addr=0,
            bias_begin_addr=0,
            quant_q_in=q_in,
            quant_q_out=q_out
        )

        val paras2 = GenAllPara(addr_w=StdPara.addr_w, h_w=StdPara.h_w, c_w=StdPara.c_w, id_w=StdPara.id_w, big_w=StdPara.big_w,
            width=4,
            height=4,
            wr_width=4,
            wr_height=4,
            in_chan=in_chan,
            out_chan=in_chan*2,
            para_num=StdPara.para_num,
            mask=StdPara.mask,
            big_begin_addr=StdPara.big_conv1_addr,
            big_max_addr=StdPara.big_global_addr,
            big_min_addr=0,
            wr_big_begin_addr=nxt_big(big_addr+in_chan*4*4/2+in_chan),
            wr_big_max_addr=StdPara.big_max_addr,
            wr_big_min_addr=StdPara.big_min_addr,
            small_begin_addr=StdPara.small_conv1_addr,
            small_max_addr=StdPara.small_global_addr,
            small_min_addr=0,
            wr_small_begin_addr=nxt_small(small_addr+in_chan*4*4/4+in_chan),
            wr_small_max_addr=StdPara.small_max_addr,
            wr_small_min_addr=StdPara.small_min_addr,
            weight_begin_addr=0,
            bias_begin_addr=0,
            quant_q_in=q_in2,
            quant_q_out=q_out2
        )

        big_addr = nxt_big(big_addr+in_chan*4*4/2)
        small_addr = nxt_small(small_addr+in_chan*4*4/4)
        when(state===(num_stages).U){
            paras.set_copy_reader1(reader1, 1)
            paras.set_copy_reader2(reader2, 1)
            paras.set_copy_read_pack(read_pack)
            paras.set_copy_switch(read_switch)
            paras.set_unuse_accu(accu)
            paras.set_quant(quant)
            paras.set_write_alar(writer, 0, in_chan-1)
            counter.set((in_chan*4*4-1).U)
            state := (num_stages+1).U
        }
        num_stages += 1
        when(state===(num_stages).U){
            when(writer.valid_out){
                when(counter.inc()){
                    state := (num_stages+1).U
                }
            }
            reader1.valid_in := true.B
            reader2.valid_in := true.B
        }
        num_stages += 1
        when(state===(num_stages).U){
            paras2.set_copy_reader1(reader1, 1)
            paras2.set_copy_reader2(reader2, 1)
            paras2.set_copy_read_pack(read_pack)
            paras2.set_conv_read_switch(read_switch)
            paras2.set_unuse_accu(accu)
            paras2.set_quant(quant)
            paras2.set_write_alar(writer, in_chan, 2*in_chan-1)
            counter.set((in_chan*4*4-1).U)
            state := (num_stages+1).U
        }
        num_stages += 1
        when(state===(num_stages).U){
            when(writer.valid_out){
                when(counter.inc()){
                    state := (num_stages+1).U
                }
            }
            reader1.valid_in := true.B
            reader2.valid_in := true.B
            calc8x8.flag := CalcType.empty
        }
        num_stages += 1
    }

    def CONV(w: Int, h: Int, in_chan: Int, out_chan: Int, conv_q_in: Int,
                        conv_q_out: Int): Unit = {
        val paras = GenAllPara(addr_w=StdPara.addr_w, h_w=StdPara.h_w, c_w=StdPara.c_w, id_w=StdPara.id_w, big_w=StdPara.big_w,
            width=w,
            height=h,
            wr_width=w,
            wr_height=h,
            in_chan=in_chan,
            out_chan=out_chan,
            para_num=StdPara.para_num,
            mask=StdPara.mask,
            big_begin_addr=big_addr,
            big_max_addr=StdPara.big_max_addr,
            big_min_addr=StdPara.big_min_addr,
            wr_big_begin_addr=nxt_big(big_addr+in_chan*w*h/2),
            wr_big_max_addr=StdPara.big_max_addr,
            wr_big_min_addr=StdPara.big_min_addr,
            small_begin_addr=small_addr,
            small_max_addr=StdPara.small_max_addr,
            small_min_addr=StdPara.small_min_addr,
            wr_small_begin_addr=nxt_small(small_addr+in_chan*w*h/4),
            wr_small_max_addr=StdPara.small_max_addr,
            wr_small_min_addr=StdPara.small_min_addr,
            weight_begin_addr=weight_addr,
            bias_begin_addr=bias_addr,
            quant_q_in=conv_q_in,
            quant_q_out=conv_q_out
        )
        big_addr = nxt_big(big_addr+in_chan*w*h/2)
        small_addr = nxt_small(small_addr+in_chan*w*h/4)
        weight_addr += in_chan*out_chan/StdPara.para_num
        bias_addr += out_chan/StdPara.para_num

        when(state===(num_stages).U){
            paras.set_conv_reader1(reader1)
            paras.set_conv_reader2(reader2)
            paras.set_read_weight(read_weight)
            paras.set_conv_read_pack(read_pack)
            paras.set_conv_accu(accu)
            paras.set_quant(quant)
            paras.set_write(writer)
            paras.set_conv_read_switch(read_switch)

            counter.set((out_chan*w*h-1).U)
            state := (num_stages+1).U
        }
        num_stages += 1
        when(state===(num_stages).U){
            when(writer.valid_out){
                when(counter.inc()){
                    state := (num_stages+1).U
                }
            }
            reader1.valid_in := true.B
            reader2.valid_in := true.B
            read_weight.valid_in := true.B
            calc8x8.flag := CalcType.calcConv
            calc8x8.mask := paras.mask.U
        }
        num_stages += 1
    }

    def CONV1(w: Int, h: Int, in_chan: Int, out_chan: Int, conv_q_in: Int,
                        conv_q_out: Int): Unit = {
        val paras = GenAllPara(addr_w=StdPara.addr_w, h_w=StdPara.h_w, c_w=StdPara.c_w, id_w=StdPara.id_w, big_w=StdPara.big_w,
            width=w,
            height=h,
            wr_width=w,
            wr_height=h,
            in_chan=in_chan,
            out_chan=out_chan,
            para_num=StdPara.para_num,
            mask=StdPara.mask,
            big_begin_addr=StdPara.big_conv0_addr,
            big_max_addr=StdPara.big_global_addr,
            big_min_addr=0,
            wr_big_begin_addr=big_addr,
            wr_big_max_addr=StdPara.big_max_addr,
            wr_big_min_addr=StdPara.big_min_addr,
            small_begin_addr=StdPara.small_conv0_addr,
            small_max_addr=StdPara.small_global_addr,
            small_min_addr=0,
            wr_small_begin_addr=small_addr,
            wr_small_max_addr=StdPara.small_max_addr,
            wr_small_min_addr=StdPara.small_min_addr,
            weight_begin_addr=weight_addr,
            bias_begin_addr=bias_addr,
            quant_q_in=conv_q_in,
            quant_q_out=conv_q_out
        )
        weight_addr += in_chan*out_chan/StdPara.para_num
        bias_addr += out_chan/StdPara.para_num

        when(state===(num_stages).U){
            paras.set_conv_reader1(reader1)
            paras.set_conv_reader2(reader2)
            paras.set_read_weight(read_weight)
            paras.set_conv_read_pack(read_pack)
            paras.set_conv_accu(accu)
            paras.set_quant(quant)
            paras.set_write(writer)
            paras.set_conv_read_switch(read_switch)

            calc8x8.mask := paras.mask.U
            counter.set((out_chan*w*h-1).U)
            state := (num_stages+1).U
        }
        num_stages += 1
        when(state===(num_stages).U){
            when(writer.valid_out){
                when(counter.inc()){
                    state := (num_stages+1).U
                }
            }
            reader1.valid_in := true.B
            reader2.valid_in := true.B
            read_weight.valid_in := true.B
            calc8x8.flag := CalcType.calcConv
            calc8x8.mask := paras.mask.U
        }
        num_stages += 1
    }

    def RELU0(w: Int, h: Int, in_chan: Int, relu_q_in: Int, relu_q_out: Int): Unit = {
        val paras = GenAllPara(addr_w=StdPara.addr_w, h_w=StdPara.h_w, c_w=StdPara.c_w, id_w=StdPara.id_w, big_w=StdPara.big_w,
            width=w,
            height=h,
            wr_width=w,
            wr_height=h,
            in_chan=in_chan,
            out_chan=in_chan,
            para_num=1,
            mask=1,
            big_begin_addr=big_addr,
            big_max_addr=StdPara.big_max_addr,
            big_min_addr=StdPara.big_min_addr,
            wr_big_begin_addr=StdPara.big_conv0_addr,
            wr_big_max_addr=StdPara.big_global_addr,
            wr_big_min_addr=0,
            small_begin_addr=small_addr,
            small_max_addr=StdPara.small_max_addr,
            small_min_addr=StdPara.small_min_addr,
            wr_small_begin_addr=StdPara.small_conv0_addr,
            wr_small_max_addr=StdPara.small_global_addr,
            wr_small_min_addr=0,
            weight_begin_addr=0,
            bias_begin_addr=0,
            quant_q_in=relu_q_in,
            quant_q_out=relu_q_out
        )

        when(state===(num_stages).U){
            paras.set_copy_reader1(reader1, 1)
            paras.set_copy_reader2(reader2, 1)
            paras.set_copy_read_pack(read_pack)
            paras.set_conv_read_switch(read_switch)
            paras.set_unuse_accu(accu)
            paras.set_quant(quant)
            paras.set_write(writer)
            counter.set((in_chan*w*h-1).U)
            state := (num_stages+1).U
        }
        num_stages += 1
        when(state===(num_stages).U){
            when(writer.valid_out){
                when(counter.inc()){
                    state := (num_stages+1).U
                }
            }
            reader1.valid_in := true.B
            reader2.valid_in := true.B
            calc8x8.flag := CalcType.leakyReLU
        }
        num_stages += 1
    }

    def RELU1(w: Int, h: Int, in_chan: Int, relu_q_in: Int, relu_q_out: Int): Unit = {
        val paras = GenAllPara(addr_w=StdPara.addr_w, h_w=StdPara.h_w, c_w=StdPara.c_w, id_w=StdPara.id_w, big_w=StdPara.big_w,
            width=w,
            height=h,
            wr_width=w,
            wr_height=h,
            in_chan=in_chan,
            out_chan=in_chan,
            para_num=1,
            mask=1,
            big_begin_addr=big_addr,
            big_max_addr=StdPara.big_max_addr,
            big_min_addr=StdPara.big_min_addr,
            wr_big_begin_addr=StdPara.big_conv1_addr,
            wr_big_max_addr=StdPara.big_global_addr,
            wr_big_min_addr=0,
            small_begin_addr=small_addr,
            small_max_addr=StdPara.small_max_addr,
            small_min_addr=StdPara.small_min_addr,
            wr_small_begin_addr=StdPara.small_conv1_addr,
            wr_small_max_addr=StdPara.small_global_addr,
            wr_small_min_addr=0,
            weight_begin_addr=0,
            bias_begin_addr=0,
            quant_q_in=relu_q_in,
            quant_q_out=relu_q_out
        )

        when(state===(num_stages).U){
            paras.set_copy_reader1(reader1, 1)
            paras.set_copy_reader2(reader2, 1)
            paras.set_copy_read_pack(read_pack)
            paras.set_conv_read_switch(read_switch)
            paras.set_unuse_accu(accu)
            paras.set_quant(quant)
            paras.set_write(writer)
            counter.set((in_chan*w*h-1).U)
            state := (num_stages+1).U
        }
        num_stages += 1
        when(state===(num_stages).U){
            when(writer.valid_out){
                when(counter.inc()){
                    state := (num_stages+1).U
                }
            }
            reader1.valid_in := true.B
            reader2.valid_in := true.B
            calc8x8.flag := CalcType.leakyReLU
        }
        num_stages += 1
    }

    def RELU(w: Int, h: Int, in_chan: Int, relu_q_in: Int, relu_q_out: Int): Unit = {
        val paras = GenAllPara(addr_w=StdPara.addr_w, h_w=StdPara.h_w, c_w=StdPara.c_w, id_w=StdPara.id_w, big_w=StdPara.big_w,
            width=w,
            height=h,
            wr_width=w,
            wr_height=h,
            in_chan=in_chan,
            out_chan=in_chan,
            para_num=StdPara.para_num,
            mask=StdPara.mask,
            big_begin_addr=big_addr,
            big_max_addr=StdPara.big_max_addr,
            big_min_addr=StdPara.big_min_addr,
            wr_big_begin_addr=nxt_big(big_addr+in_chan*w*h/2),
            wr_big_max_addr=StdPara.big_max_addr,
            wr_big_min_addr=StdPara.big_min_addr,
            small_begin_addr=small_addr,
            small_max_addr=StdPara.small_max_addr,
            small_min_addr=StdPara.small_min_addr,
            wr_small_begin_addr=nxt_small(small_addr+in_chan*w*h/4),
            wr_small_max_addr=StdPara.small_max_addr,
            wr_small_min_addr=StdPara.small_min_addr,
            weight_begin_addr=0,
            bias_begin_addr=0,
            quant_q_in=relu_q_in,
            quant_q_out=relu_q_out
        )

        big_addr = nxt_big(big_addr+in_chan*w*h/2)
        small_addr = nxt_small(small_addr+in_chan*w*h/4)
        when(state===(num_stages).U){
            paras.set_copy_reader1(reader1, 1)
            paras.set_copy_reader2(reader2, 1)
            paras.set_copy_read_pack(read_pack)
            paras.set_conv_read_switch(read_switch)
            paras.set_unuse_accu(accu)
            paras.set_quant(quant)
            paras.set_write(writer)
            counter.set((in_chan*w*h-1).U)
            state := (num_stages+1).U
        }
        num_stages += 1
        when(state===(num_stages).U){
            when(writer.valid_out){
                when(counter.inc()){
                    state := (num_stages+1).U
                }
            }
            reader1.valid_in := true.B
            reader2.valid_in := true.B
            calc8x8.flag := CalcType.leakyReLU
        }
        num_stages += 1
    }

    def MAXPOOL(w: Int, h: Int, in_chan: Int): Unit = {
        val paras = GenAllPara(addr_w=StdPara.addr_w, h_w=StdPara.h_w, c_w=StdPara.c_w, id_w=StdPara.id_w, big_w=StdPara.big_w,
            width=w,
            height=h,
            wr_width=w/2,
            wr_height=h/2,
            in_chan=in_chan,
            out_chan=in_chan,
            para_num=1,
            mask=0,
            big_begin_addr=StdPara.big_conv1_addr,
            big_max_addr=StdPara.big_global_addr,
            big_min_addr=0,
            wr_big_begin_addr=big_addr,
            wr_big_max_addr=StdPara.big_max_addr,
            wr_big_min_addr=StdPara.big_min_addr,
            small_begin_addr=StdPara.small_conv1_addr,
            small_max_addr=StdPara.small_global_addr,
            small_min_addr=0,
            wr_small_begin_addr=small_addr,
            wr_small_max_addr=StdPara.small_max_addr,
            wr_small_min_addr=StdPara.small_min_addr,
            weight_begin_addr=0,
            bias_begin_addr=0,
            quant_q_in=0,
            quant_q_out=0
        )

        when(state===(num_stages).U){
            paras.set_maxp_reader1(reader1)
            paras.set_maxp_reader2(reader2)
            paras.set_write(writer)
            paras.set_maxpool_switch(read_switch)
            counter.set((in_chan*w*h/4-1).U)

            state := (num_stages+1).U
        }
        num_stages += 1
        when(state===(num_stages).U){
            when(writer.valid_out){
                when(counter.inc()){
                    state := (num_stages+1).U
                }
            }
            reader1.valid_in := true.B
            reader2.valid_in := true.B
        }
        num_stages += 1
    }

    def UPS(w: Int, h: Int, in_chan: Int): Unit = {
        val paras = GenAllPara(addr_w=StdPara.addr_w, h_w=StdPara.h_w, c_w=StdPara.c_w, id_w=StdPara.id_w, big_w=StdPara.big_w,
            width=w,
            height=h,
            wr_width=w*2,
            wr_height=h*2,
            in_chan=in_chan,
            out_chan=in_chan,
            para_num=StdPara.para_num,
            mask=StdPara.mask,
            big_begin_addr=big_addr,
            big_max_addr=StdPara.big_max_addr,
            big_min_addr=StdPara.big_min_addr,
            wr_big_begin_addr=nxt_big(big_addr+in_chan*w*h/2),
            wr_big_max_addr=StdPara.big_max_addr,
            wr_big_min_addr=StdPara.big_min_addr,
            small_begin_addr=small_addr,
            small_max_addr=StdPara.small_max_addr,
            small_min_addr=StdPara.small_min_addr,
            wr_small_begin_addr=nxt_small(small_addr+in_chan*w*h/4),
            wr_small_max_addr=StdPara.small_max_addr,
            wr_small_min_addr=StdPara.small_min_addr,
            weight_begin_addr=0,
            bias_begin_addr=0,
            quant_q_in=0,
            quant_q_out=0
        )

        big_addr = nxt_big(big_addr+in_chan*w*h/2)
        small_addr = nxt_small(small_addr+in_chan*w*h/4)

        when(state===(num_stages).U){
            paras.set_ups_reader1(reader1)
            paras.set_ups_reader2(reader2)
            paras.set_ups_read_pack(read_pack)
            paras.set_write(writer)
            paras.set_ups_switch(read_switch)
            counter.set((in_chan*w*h*4-1).U)

            state := (num_stages+1).U
        }
        num_stages += 1
        when(state===(num_stages).U){
            when(writer.valid_out){
                when(counter.inc()){
                    state := (num_stages+1).U
                }
            }
            reader1.valid_in := true.B
            reader2.valid_in := true.B
        }
        num_stages += 1
    }

    def LAST_PRINT(w: Int, h: Int, in_chan: Int): Unit = {
        val output_cache = RegInit(VecInit(Seq.fill(64)(0.S(16.W))))
        val write_cache = RegInit(VecInit(Seq.fill(64)(0.S(16.W))))
        val lst_counter = RegInit(0.U.asTypeOf(ACounter(16.W)))
        val lst_64_counter = RegInit(0.U.asTypeOf(ACounter(16.W)))
        println(big_addr)
        println(small_addr)
        val paras_print = GenAllPara(StdPara.addr_w, StdPara.h_w, StdPara.c_w, StdPara.id_w, big_w=StdPara.big_w,
            8, 8, 4, 4,
            4, 1,
            1, 0,
            big_addr, StdPara.big_max_addr, StdPara.big_min_addr,
            0, 0, 0,
            small_addr, StdPara.small_max_addr, StdPara.small_min_addr,
            0, 0, 0,
            0, 0, 0, 0
        )
        val paras_new_input = GenAllPara(StdPara.addr_w, StdPara.h_w, StdPara.c_w, StdPara.id_w, big_w=StdPara.big_w,
            8, 8, 4, 4,
            4, 4,
            1, 0,
            0, 0, 0,
            StdPara.big_min_addr, StdPara.big_max_addr, StdPara.big_min_addr,
            0, 0, 0,
            StdPara.small_min_addr, StdPara.small_max_addr, StdPara.small_min_addr,
            0, 0, 0, 0
        )
        when(state===(num_stages).U){
            state := (num_stages+1).U
            paras_print.set_conv_reader1(reader1)
            paras_print.set_conv_reader2(reader2)
            paras_print.set_conv_read_pack(read_pack)
            read_switch.flag_job := true.B
            read_switch.job := ReadSwitchType.idle
            paras_new_input.set_write(writer)
            lst_64_counter.set(63.U)
            lst_counter.set((w*h*in_chan-1).U)
        }
        when(state===(num_stages+1).U){
            reader1.valid_in := true.B
            reader2.valid_in := true.B
            state := (num_stages+2).U
        }
        when(state===(num_stages+2).U){
            state := (num_stages+3).U
            io.complete := true.B
        }
        for(i <- 0 to 62)
            write_cache(i) := write_cache(i+1)
        val rs_reg = RegInit(false.B)
        when(state===(num_stages+3).U){
            read_switch.data_from_user.mat := write_cache
            rs_reg := false.B
            when(lst_64_counter.inc()){
                when(lst_counter.inc()){
                    state := (num_stages+4).U
                }
                rs_reg := true.B
            }.elsewhen(lst_64_counter.ccnt===62.U){
                reader1.valid_in := true.B
                reader2.valid_in := true.B
            }
            read_switch.valid_in_from_user := rs_reg
            when(read_pack.valid_out){
                io.valid := true.B
                io.output := read_pack.output.mat(0)
                for(i <- 0 to 62)
                    output_cache(i) := read_pack.output.mat(i+1)
            }.otherwise{
                io.valid := true.B
                io.output := output_cache(0)
                for(i <- 0 to 62)
                    output_cache(i) := output_cache(i+1)
            }
            write_cache(63) := io.input
        }
        when(state===(num_stages+4).U){
            rs_reg := false.B
            state := (num_stages+5).U
            io.complete_output := true.B
        }
        num_stages += 5
    }

    /*def CHECK_PRINT(): Unit = {
        when(io.valid){
            checksum := checksum^io.output.asUInt
        }
        io.checksum := checksum
    }*/

    def NEXT_LOOP(): Unit = {
        println("next_loop: ", num_stages)
        when(state===(num_stages).U){
            state := 0.U
        }
        num_stages += 1
    }
    def END(): Unit = {
        num_stages += 1
    }

    println("all state: ", num_stages)
}

val rom_weight = if(StdPara.para_num==4) Module(new ROMWeight(StdPara.para_num)).io else Module(new ROMHalfWeight(StdPara.para_num)).io


package final_project

import chisel3._
import chisel3.util._

import chisel3.experimental.BundleLiterals._

class Show extends Module{
    val io = IO(new Bundle{
        val start = Input(Bool())
        val ok = Output(Bool())
    })
    val fuck = Module(new Top).io
    fuck.start := io.start
    fuck.input := 0.S
    io.ok := false.B

    when(fuck.complete_output){
        io.ok := fuck.checksum===26782.U
    }


package final_project

import chisel3._
import chisel3.util._

import chisel3.experimental.BundleLiterals._

class Top extends Module{
    //val io = IO(new)
    val io = IO(new Bundle{
        val start = Input(Bool())
        val input = Input(SInt(16.W))
        val complete = Output(Bool())
        val valid = Output(Bool())
        val complete_output = Output(Bool())
        // val output = Output(UInt(1.W))
        val output = Output(SInt(16.W))
        val checksum = Output(UInt(16.W))
    })

    val reader1 = Module(new GraphReader(StdPara.addr_w, StdPara.h_w, StdPara.c_w, StdPara.id_w, StdPara.big_w)).io
    val reader2 = Module(new GraphReader(StdPara.addr_w, StdPara.h_w, StdPara.c_w, StdPara.id_w, StdPara.big_w)).io
    val read_pack = Module(new PackReadData(StdPara.w, StdPara.h_w, StdPara.c_w, StdPara.big_w)).io
    val read_switch = Module(new ReadSwitch(StdPara.w, StdPara.h_w, StdPara.c_w, StdPara.para_num)).io
    val read_weight = Module(new WeightReader(StdPara.w, StdPara.weight_addr_w)).io
    val calc8x8 = Module(new Calc8x8(StdPara.w, StdPara.para_num)).io
    val accu = Module(new Accumu(StdPara.w, StdPara.bias_addr_w, StdPara.bias_w, StdPara.para_num)).io

    val cache_writer = Module(new CacheWriter(StdPara.w, StdPara.para_num)).io
    val quant = Module(new Quant(StdPara.w)).io
    val write_switch = Module(new WriteSwitch(StdPara.w, 3)).io
    val writer = Module(new RealWriter(StdPara.w, StdPara.addr_w, StdPara.h_w, StdPara.c_w, StdPara.id_w)).io
    val maxpool = Module(new Maxpool(StdPara.w)).io

    val rom_weight = if(StdPara.para_num==4) Module(new ROMWeight(StdPara.para_num)).io else Module(new ROMHalfWeight(StdPara.para_num)).io

    // val rom_weight = Module(new ROMWeight(StdPara.para_num)).io
    // val rom_bias = Module(new ROMBias(StdPara.para_num)).io
    val bram = Module(new RAMGroup(StdPara.w, StdPara.addr_w, StdPara.id_w)).io

    io.complete := false.B
    io.valid := false.B
    io.output := 0.S
    bram.rd_addr1 := reader1.to_banks
    reader1.flag_job := false.B
    reader1.valid_in := false.B
    reader1.job := 0.U.asTypeOf(reader1.job)
    reader1.job_type := 0.U.asTypeOf(reader1.job_type)
    reader1.job2 := 0.U.asTypeOf(reader1.job2)
    reader1.signal := false.B

    bram.rd_valid_in := reader2.valid_out
    bram.rd_addr2 := reader2.to_banks
    reader2.flag_job := false.B
    reader2.valid_in := false.B
    reader2.job := 0.U.asTypeOf(reader2.job)
    reader2.job_type := 0.U.asTypeOf(reader2.job_type)
    reader2.job2 := 0.U.asTypeOf(reader2.job2)
    reader2.signal := false.B

    read_weight.valid_in := false.B
    read_weight.addr_end := 0.U
    read_weight.begin_addr := 0.U
    rom_weight.addr := read_weight.addr
    read_weight.flag_job := false.B

    read_pack.valid_in := bram.rd_valid_out
    read_pack.flag_job := false.B
    read_pack.job := 0.U.asTypeOf(read_pack.job)
    read_pack.from_big := bram.rd_big
    read_pack.from_small := bram.rd_small

    read_switch.flag_job := false.B
    read_switch.job := 0.U.asTypeOf(read_switch.job)
    read_switch.valid_in := read_pack.valid_out
    read_switch.from := read_pack.output
    read_switch.from_weight := rom_weight.out
    read_switch.in_h := 0.U
    read_switch.in_chan := 0.U
    read_switch.valid_in_from_user := false.B
    read_switch.data_from_user := 0.U.asTypeOf(read_switch.data_from_user)

    calc8x8.input := read_switch.to_calc8x8
    calc8x8.flag := 0.U.asTypeOf(calc8x8.flag)
    calc8x8.weight := read_switch.to_weight
    calc8x8.valid_in := read_switch.valid_out_calc8x8
    calc8x8.mask := 0.U
    calc8x8.multmap := read_switch.to_multmap

    accu.in_from_calc8x8 := calc8x8.output
    accu.valid_in := calc8x8.valid_out
    accu.flag_job := false.B
    accu.csum := 0.U
    accu.bias_begin_addr := 0.U
    accu.bias_end_addr := 0.U
    rom_bias.addr := accu.bias_addr
    accu.bias_in := rom_bias.out
    accu.is_in_use := false.B

    cache_writer.in_from_accumu := accu.result
    cache_writer.valid_in := accu.valid_out

    quant.in_from_accumu := cache_writer.out
    quant.valid_in := cache_writer.valid_out
    quant.flag_job := false.B
    quant.quant_in := 0.U.asTypeOf(quant.quant_in)

    maxpool.from_big := bram.rd_big
    for(t <- 0 to 1){
        maxpool.from_small(t)(0) := bram.rd_small(t)(0)
        maxpool.from_small(t)(1) := bram.rd_small(t)(1)
    }

    maxpool.valid_in := read_switch.valid_out_maxp

    //write_switch.valid_in(1) := false.B
    //write_switch.input(1) := 0.U.asTypeOf(write_switch.input(1))

    write_switch.valid_in(0) := quant.valid_out // conv
    write_switch.valid_in(1) := maxpool.valid_out // maxpool
    write_switch.valid_in(2) := read_switch.valid_out_copy // go through

    write_switch.input(0) := quant.result
    write_switch.input(1) := maxpool.result
    write_switch.input(2) := read_switch.to_copy

    writer.in_from_quant := write_switch.output
    writer.valid_in := write_switch.valid_out
    writer.flag_job := false.B
    writer.job := 0.U.asTypeOf(writer.job)

    // io.output_ls := quant.result.mat(0)

    /*dontTouch(writer.to_banks)
    dontTouch(writer.to_bigbank)
    dontTouch(writer.to_smallbank)
    dontTouch(writer.valid_out)*/
    bram.wr_addr := writer.to_banks
    bram.to_bigbank := writer.to_bigbank
    bram.to_smallbank := writer.to_smallbank
    bram.wr_valid_in := writer.valid_out

    def nxt_big(x: Int): Int = {
        val L = StdPara.big_min_addr
        val R = StdPara.big_max_addr
        return (x-L)%(R-L+1)+L
    }
    def nxt_small(x: Int): Int = {
        val L = StdPara.small_min_addr
        val R = StdPara.small_max_addr
        return (x-L)%(R-L+1)+L
    }

    var big_addr = 0
    var small_addr = 0
    var weight_addr = 0
    var bias_addr = 0

    val counter = RegInit(0.U.asTypeOf(ACounter(16.W)))
    val state = RegInit(0.U(8.W))

    val output_cache = RegInit(VecInit(Seq.fill(64)(0.S(16.W))))

    var num_stages = 0

    io.complete_output := false.B

    WAIT_START()
    CONV(4, 4, 4, 16, 30, 15)
    // WRITEHALF_CONV1(8, 8, 17, 8)
    // LAST_PRINT(4, 4, 32)

    // CONV(4, 4, 16, 32, 32, 16)
    // RELU(4, 4, 16, 15+15, 17)
    // LAST_PRINT(4, 4, 16)
    RELU0(4, 4, 16, 15+15, 17)
    // weight_addr = 0
    // bias_addr = 0
    // big_addr = 0
    // small_addr = 0
    // CONV(4, 4, 4, 16, 30, 15)
    // RELU(4, 4, 16, 15+15, 17)
    // LAST_PRINT(4, 4, 16)

    CONV1(4, 4, 16, 32, 32, 16)
    RELU1(4, 4, 32, 16+15, 17)
    MAXPOOL(4, 4, 32)
    //conv_2
    CONV(2, 2, 32, 64, 32, 14)
    RELU(2, 2, 64, 14+15, 14)

    // conv_6
    CONV(2, 2, 64, 64, 29, 11)
    RELU(2, 2, 64, 11+15, 11)

    // conv_u1
    UPS(2, 2, 64)

    CONV(4, 4, 64, 32, 26, 8)
    RELU(4, 4, 32, 8+15, 8)
    // LAST_PRINT(4, 4, 32)
    println(num_stages)
    CONVMULT()
    println(num_stages)

    // conv_u4
    UPS(4, 4, 16)
    CONV(8, 8, 16, 16, 27, 12)
    RELU(8, 8, 16, 12+15, 12)

    // conv_10
    CONV(8, 8, 16, 4, 29, 13)
    LAST_PRINT(8, 8, 4)

    val checksum = RegInit(0.U(16.W))
    CHECK_PRINT()
    // LAST_CHECK(8, 8, 4)
    // LAST_PRINT(2, 2, 32)

    def WAIT_START(){
        when(state===(num_stages).U){
            when(io.start){
                state := (num_stages+1).U
            }
        }
        num_stages += 1
    }
    def CONVMULT(){
        WRITEHALF_CONV1(8, 8, 17, 8)
        println(num_stages)
        CONV(4, 4, 64, 16, 22, 6)
        println(num_stages)
        RELU(4, 4, 16, 6+15, 6)
        println(num_stages)
        MULT(6+17, 11)
        // LAST_PRINT(4, 4, 16)
    }

    def MULT(q_in: Int, q_out: Int){
        val paras = GenAllPara(addr_w=StdPara.addr_w, h_w=StdPara.h_w, c_w=StdPara.c_w, id_w=StdPara.id_w, big_w=StdPara.big_w,
            width=4,
            height=4,
            wr_width=4,
            wr_height=4,
            in_chan=16,
            out_chan=16,
            para_num=StdPara.para_num,
            mask=StdPara.mask,
            big_begin_addr=big_addr,
            big_max_addr=StdPara.big_max_addr,
            big_min_addr=StdPara.big_min_addr,
            wr_big_begin_addr=nxt_big(big_addr+16*4*4/2),
            wr_big_max_addr=StdPara.big_max_addr,
            wr_big_min_addr=StdPara.big_min_addr,
            small_begin_addr=small_addr,
            small_max_addr=StdPara.small_max_addr,
            small_min_addr=StdPara.small_min_addr,
            wr_small_begin_addr=nxt_small(small_addr+16*4*4/4),
            wr_small_max_addr=StdPara.small_max_addr,
            wr_small_min_addr=StdPara.small_min_addr,
            weight_begin_addr=0,
            bias_begin_addr=0,
            quant_q_in=q_in,
            quant_q_out=q_out
        )

        val paras2 = GenAllPara(addr_w=StdPara.addr_w, h_w=StdPara.h_w, c_w=StdPara.c_w, id_w=StdPara.id_w, big_w=StdPara.big_w,
            width=4,
            height=4,
            wr_width=4,
            wr_height=4,
            in_chan=16,
            out_chan=16,
            para_num=StdPara.para_num,
            mask=StdPara.mask,
            big_begin_addr=StdPara.big_conv0_addr,
            big_max_addr=StdPara.big_global_addr,
            big_min_addr=0,
            wr_big_begin_addr=0,
            wr_big_max_addr=0,
            wr_big_min_addr=0,
            small_begin_addr=StdPara.small_conv0_addr,
            small_max_addr=StdPara.small_global_addr,
            small_min_addr=0,
            wr_small_begin_addr=0,
            wr_small_max_addr=0,
            wr_small_min_addr=0,
            weight_begin_addr=0,
            bias_begin_addr=0,
            quant_q_in=0,
            quant_q_out=0
        )

        big_addr = nxt_big(big_addr+16*4*4/2)
        small_addr = nxt_small(small_addr+16*4*4/4)
        when(state===(num_stages).U){
            paras.set_copy_reader1(reader1, 1)
            paras.set_copy_reader2(reader2, 1)
            paras2.set_copy_reader1_job2(reader1, 1)
            paras2.set_copy_reader2_job2(reader2, 1)
            paras.set_ups_read_pack(read_pack)
            paras.set_mult_switch(read_switch)
            paras.set_unuse_accu(accu)
            paras.set_quant(quant)
            paras.set_write(writer)
            counter.set((16*4*4-1).U)
            state := (num_stages+1).U
        }
        num_stages += 1
        when(state===(num_stages).U){
            when(writer.valid_out){
                when(counter.inc()){
                    state := (num_stages+1).U
                }
            }
            reader1.valid_in := true.B
            reader2.valid_in := true.B
            reader1.signal := true.B
            reader2.signal := true.B
            calc8x8.flag := CalcType.calcMult
        }
        num_stages += 1

    }

    def WRITEHALF_CONV1(q_in: Int, q_out: Int, q_in2: Int, q_out2: Int){
        val in_chan = 32
        val paras = GenAllPara(addr_w=StdPara.addr_w, h_w=StdPara.h_w, c_w=StdPara.c_w, id_w=StdPara.id_w, big_w=StdPara.big_w,
            width=4,
            height=4,
            wr_width=4,
            wr_height=4,
            in_chan=in_chan,
            out_chan=in_chan*2,
            para_num=StdPara.para_num,
            mask=StdPara.mask,
            big_begin_addr=big_addr,
            big_max_addr=StdPara.big_max_addr,
            big_min_addr=StdPara.big_min_addr,
            wr_big_begin_addr=nxt_big(big_addr+in_chan*4*4/2+0),
            wr_big_max_addr=StdPara.big_max_addr,
            wr_big_min_addr=StdPara.big_min_addr,
            small_begin_addr=small_addr,
            small_max_addr=StdPara.small_max_addr,
            small_min_addr=StdPara.small_min_addr,
            wr_small_begin_addr=nxt_small(small_addr+in_chan*4*4/4+0),
            wr_small_max_addr=StdPara.small_max_addr,
            wr_small_min_addr=StdPara.small_min_addr,
            weight_begin_addr=0,
            bias_begin_addr=0,
            quant_q_in=q_in,
            quant_q_out=q_out
        )

        val paras2 = GenAllPara(addr_w=StdPara.addr_w, h_w=StdPara.h_w, c_w=StdPara.c_w, id_w=StdPara.id_w, big_w=StdPara.big_w,
            width=4,
            height=4,
            wr_width=4,
            wr_height=4,
            in_chan=in_chan,
            out_chan=in_chan*2,
            para_num=StdPara.para_num,
            mask=StdPara.mask,
            big_begin_addr=StdPara.big_conv1_addr,
            big_max_addr=StdPara.big_global_addr,
            big_min_addr=0,
            wr_big_begin_addr=nxt_big(big_addr+in_chan*4*4/2+in_chan),
            wr_big_max_addr=StdPara.big_max_addr,
            wr_big_min_addr=StdPara.big_min_addr,
            small_begin_addr=StdPara.small_conv1_addr,
            small_max_addr=StdPara.small_global_addr,
            small_min_addr=0,
            wr_small_begin_addr=nxt_small(small_addr+in_chan*4*4/4+in_chan),
            wr_small_max_addr=StdPara.small_max_addr,
            wr_small_min_addr=StdPara.small_min_addr,
            weight_begin_addr=0,
            bias_begin_addr=0,
            quant_q_in=q_in2,
            quant_q_out=q_out2
        )

        big_addr = nxt_big(big_addr+in_chan*4*4/2)
        small_addr = nxt_small(small_addr+in_chan*4*4/4)
        when(state===(num_stages).U){
            paras.set_copy_reader1(reader1, 1)
            paras.set_copy_reader2(reader2, 1)
            paras.set_copy_read_pack(read_pack)
            paras.set_copy_switch(read_switch)
            paras.set_unuse_accu(accu)
            paras.set_quant(quant)
            paras.set_write_alar(writer, 0, in_chan-1)
            counter.set((in_chan*4*4-1).U)
            state := (num_stages+1).U
        }
        num_stages += 1
        when(state===(num_stages).U){
            when(writer.valid_out){
                when(counter.inc()){
                    state := (num_stages+1).U
                }
            }
            reader1.valid_in := true.B
            reader2.valid_in := true.B
        }
        num_stages += 1
        when(state===(num_stages).U){
            paras2.set_copy_reader1(reader1, 1)
            paras2.set_copy_reader2(reader2, 1)
            paras2.set_copy_read_pack(read_pack)
            paras2.set_conv_read_switch(read_switch)
            paras2.set_unuse_accu(accu)
            paras2.set_quant(quant)
            paras2.set_write_alar(writer, in_chan, 2*in_chan-1)
            counter.set((in_chan*4*4-1).U)
            state := (num_stages+1).U
        }
        num_stages += 1
        when(state===(num_stages).U){
            when(writer.valid_out){
                when(counter.inc()){
                    state := (num_stages+1).U
                }
            }
            reader1.valid_in := true.B
            reader2.valid_in := true.B
            calc8x8.flag := CalcType.empty
        }
        num_stages += 1
    }

    def CONV(w: Int, h: Int, in_chan: Int, out_chan: Int, conv_q_in: Int,
                        conv_q_out: Int): Unit = {
        val paras = GenAllPara(addr_w=StdPara.addr_w, h_w=StdPara.h_w, c_w=StdPara.c_w, id_w=StdPara.id_w, big_w=StdPara.big_w,
            width=w,
            height=h,
            wr_width=w,
            wr_height=h,
            in_chan=in_chan,
            out_chan=out_chan,
            para_num=StdPara.para_num,
            mask=StdPara.mask,
            big_begin_addr=big_addr,
            big_max_addr=StdPara.big_max_addr,
            big_min_addr=StdPara.big_min_addr,
            wr_big_begin_addr=nxt_big(big_addr+in_chan*w*h/2),
            wr_big_max_addr=StdPara.big_max_addr,
            wr_big_min_addr=StdPara.big_min_addr,
            small_begin_addr=small_addr,
            small_max_addr=StdPara.small_max_addr,
            small_min_addr=StdPara.small_min_addr,
            wr_small_begin_addr=nxt_small(small_addr+in_chan*w*h/4),
            wr_small_max_addr=StdPara.small_max_addr,
            wr_small_min_addr=StdPara.small_min_addr,
            weight_begin_addr=weight_addr,
            bias_begin_addr=bias_addr,
            quant_q_in=conv_q_in,
            quant_q_out=conv_q_out
        )
        big_addr = nxt_big(big_addr+in_chan*w*h/2)
        small_addr = nxt_small(small_addr+in_chan*w*h/4)
        weight_addr += in_chan*out_chan/StdPara.para_num
        bias_addr += out_chan/StdPara.para_num

        when(state===(num_stages).U){
            paras.set_conv_reader1(reader1)
            paras.set_conv_reader2(reader2)
            paras.set_read_weight(read_weight)
            paras.set_conv_read_pack(read_pack)
            paras.set_conv_accu(accu)
            paras.set_quant(quant)
            paras.set_write(writer)
            paras.set_conv_read_switch(read_switch)

            counter.set((out_chan*w*h-1).U)
            state := (num_stages+1).U
        }
        num_stages += 1
        when(state===(num_stages).U){
            when(writer.valid_out){
                when(counter.inc()){
                    state := (num_stages+1).U
                }
            }
            reader1.valid_in := true.B
            reader2.valid_in := true.B
            read_weight.valid_in := true.B
            calc8x8.flag := CalcType.calcConv
            calc8x8.mask := paras.mask.U
        }
        num_stages += 1
    }

    def CONV1(w: Int, h: Int, in_chan: Int, out_chan: Int, conv_q_in: Int,
                        conv_q_out: Int): Unit = {
        val paras = GenAllPara(addr_w=StdPara.addr_w, h_w=StdPara.h_w, c_w=StdPara.c_w, id_w=StdPara.id_w, big_w=StdPara.big_w,
            width=w,
            height=h,
            wr_width=w,
            wr_height=h,
            in_chan=in_chan,
            out_chan=out_chan,
            para_num=StdPara.para_num,
            mask=StdPara.mask,
            big_begin_addr=StdPara.big_conv0_addr,
            big_max_addr=StdPara.big_global_addr,
            big_min_addr=0,
            wr_big_begin_addr=big_addr,
            wr_big_max_addr=StdPara.big_max_addr,
            wr_big_min_addr=StdPara.big_min_addr,
            small_begin_addr=StdPara.small_conv0_addr,
            small_max_addr=StdPara.small_global_addr,
            small_min_addr=0,
            wr_small_begin_addr=small_addr,
            wr_small_max_addr=StdPara.small_max_addr,
            wr_small_min_addr=StdPara.small_min_addr,
            weight_begin_addr=weight_addr,
            bias_begin_addr=bias_addr,
            quant_q_in=conv_q_in,
            quant_q_out=conv_q_out
        )
        weight_addr += in_chan*out_chan/StdPara.para_num
        bias_addr += out_chan/StdPara.para_num

        when(state===(num_stages).U){
            paras.set_conv_reader1(reader1)
            paras.set_conv_reader2(reader2)
            paras.set_read_weight(read_weight)
            paras.set_conv_read_pack(read_pack)
            paras.set_conv_accu(accu)
            paras.set_quant(quant)
            paras.set_write(writer)
            paras.set_conv_read_switch(read_switch)

            calc8x8.mask := paras.mask.U
            counter.set((out_chan*w*h-1).U)
            state := (num_stages+1).U
        }
        num_stages += 1
        when(state===(num_stages).U){
            when(writer.valid_out){
                when(counter.inc()){
                    state := (num_stages+1).U
                }
            }
            reader1.valid_in := true.B
            reader2.valid_in := true.B
            read_weight.valid_in := true.B
            calc8x8.flag := CalcType.calcConv
            calc8x8.mask := paras.mask.U
        }
        num_stages += 1
    }

    def RELU0(w: Int, h: Int, in_chan: Int, relu_q_in: Int, relu_q_out: Int): Unit = {
        val paras = GenAllPara(addr_w=StdPara.addr_w, h_w=StdPara.h_w, c_w=StdPara.c_w, id_w=StdPara.id_w, big_w=StdPara.big_w,
            width=w,
            height=h,
            wr_width=w,
            wr_height=h,
            in_chan=in_chan,
            out_chan=in_chan,
            para_num=1,
            mask=1,
            big_begin_addr=big_addr,
            big_max_addr=StdPara.big_max_addr,
            big_min_addr=StdPara.big_min_addr,
            wr_big_begin_addr=StdPara.big_conv0_addr,
            wr_big_max_addr=StdPara.big_global_addr,
            wr_big_min_addr=0,
            small_begin_addr=small_addr,
            small_max_addr=StdPara.small_max_addr,
            small_min_addr=StdPara.small_min_addr,
            wr_small_begin_addr=StdPara.small_conv0_addr,
            wr_small_max_addr=StdPara.small_global_addr,
            wr_small_min_addr=0,
            weight_begin_addr=0,
            bias_begin_addr=0,
            quant_q_in=relu_q_in,
            quant_q_out=relu_q_out
        )

        when(state===(num_stages).U){
            paras.set_copy_reader1(reader1, 1)
            paras.set_copy_reader2(reader2, 1)
            paras.set_copy_read_pack(read_pack)
            paras.set_conv_read_switch(read_switch)
            paras.set_unuse_accu(accu)
            paras.set_quant(quant)
            paras.set_write(writer)
            counter.set((in_chan*w*h-1).U)
            state := (num_stages+1).U
        }
        num_stages += 1
        when(state===(num_stages).U){
            when(writer.valid_out){
                when(counter.inc()){
                    state := (num_stages+1).U
                }
            }
            reader1.valid_in := true.B
            reader2.valid_in := true.B
            calc8x8.flag := CalcType.leakyReLU
        }
        num_stages += 1
    }

    def RELU1(w: Int, h: Int, in_chan: Int, relu_q_in: Int, relu_q_out: Int): Unit = {
        val paras = GenAllPara(addr_w=StdPara.addr_w, h_w=StdPara.h_w, c_w=StdPara.c_w, id_w=StdPara.id_w, big_w=StdPara.big_w,
            width=w,
            height=h,
            wr_width=w,
            wr_height=h,
            in_chan=in_chan,
            out_chan=in_chan,
            para_num=1,
            mask=1,
            big_begin_addr=big_addr,
            big_max_addr=StdPara.big_max_addr,
            big_min_addr=StdPara.big_min_addr,
            wr_big_begin_addr=StdPara.big_conv1_addr,
            wr_big_max_addr=StdPara.big_global_addr,
            wr_big_min_addr=0,
            small_begin_addr=small_addr,
            small_max_addr=StdPara.small_max_addr,
            small_min_addr=StdPara.small_min_addr,
            wr_small_begin_addr=StdPara.small_conv1_addr,
            wr_small_max_addr=StdPara.small_global_addr,
            wr_small_min_addr=0,
            weight_begin_addr=0,
            bias_begin_addr=0,
            quant_q_in=relu_q_in,
            quant_q_out=relu_q_out
        )

        when(state===(num_stages).U){
            paras.set_copy_reader1(reader1, 1)
            paras.set_copy_reader2(reader2, 1)
            paras.set_copy_read_pack(read_pack)
            paras.set_conv_read_switch(read_switch)
            paras.set_unuse_accu(accu)
            paras.set_quant(quant)
            paras.set_write(writer)
            counter.set((in_chan*w*h-1).U)
            state := (num_stages+1).U
        }
        num_stages += 1
        when(state===(num_stages).U){
            when(writer.valid_out){
                when(counter.inc()){
                    state := (num_stages+1).U
                }
            }
            reader1.valid_in := true.B
            reader2.valid_in := true.B
            calc8x8.flag := CalcType.leakyReLU
        }
        num_stages += 1
    }

    def RELU(w: Int, h: Int, in_chan: Int, relu_q_in: Int, relu_q_out: Int): Unit = {
        val paras = GenAllPara(addr_w=StdPara.addr_w, h_w=StdPara.h_w, c_w=StdPara.c_w, id_w=StdPara.id_w, big_w=StdPara.big_w,
            width=w,
            height=h,
            wr_width=w,
            wr_height=h,
            in_chan=in_chan,
            out_chan=in_chan,
            para_num=StdPara.para_num,
            mask=StdPara.mask,
            big_begin_addr=big_addr,
            big_max_addr=StdPara.big_max_addr,
            big_min_addr=StdPara.big_min_addr,
            wr_big_begin_addr=nxt_big(big_addr+in_chan*w*h/2),
            wr_big_max_addr=StdPara.big_max_addr,
            wr_big_min_addr=StdPara.big_min_addr,
            small_begin_addr=small_addr,
            small_max_addr=StdPara.small_max_addr,
            small_min_addr=StdPara.small_min_addr,
            wr_small_begin_addr=nxt_small(small_addr+in_chan*w*h/4),
            wr_small_max_addr=StdPara.small_max_addr,
            wr_small_min_addr=StdPara.small_min_addr,
            weight_begin_addr=0,
            bias_begin_addr=0,
            quant_q_in=relu_q_in,
            quant_q_out=relu_q_out
        )

        big_addr = nxt_big(big_addr+in_chan*w*h/2)
        small_addr = nxt_small(small_addr+in_chan*w*h/4)
        when(state===(num_stages).U){
            paras.set_copy_reader1(reader1, 1)
            paras.set_copy_reader2(reader2, 1)
            paras.set_copy_read_pack(read_pack)
            paras.set_conv_read_switch(read_switch)
            paras.set_unuse_accu(accu)
            paras.set_quant(quant)
            paras.set_write(writer)
            counter.set((in_chan*w*h-1).U)
            state := (num_stages+1).U
        }
        num_stages += 1
        when(state===(num_stages).U){
            when(writer.valid_out){
                when(counter.inc()){
                    state := (num_stages+1).U
                }
            }
            reader1.valid_in := true.B
            reader2.valid_in := true.B
            calc8x8.flag := CalcType.leakyReLU
        }
        num_stages += 1
    }

    def MAXPOOL(w: Int, h: Int, in_chan: Int): Unit = {
        val paras = GenAllPara(addr_w=StdPara.addr_w, h_w=StdPara.h_w, c_w=StdPara.c_w, id_w=StdPara.id_w, big_w=StdPara.big_w,
            width=w,
            height=h,
            wr_width=w/2,
            wr_height=h/2,
            in_chan=in_chan,
            out_chan=in_chan,
            para_num=1,
            mask=0,
            big_begin_addr=StdPara.big_conv1_addr,
            big_max_addr=StdPara.big_global_addr,
            big_min_addr=0,
            wr_big_begin_addr=big_addr,
            wr_big_max_addr=StdPara.big_max_addr,
            wr_big_min_addr=StdPara.big_min_addr,
            small_begin_addr=StdPara.small_conv1_addr,
            small_max_addr=StdPara.small_global_addr,
            small_min_addr=0,
            wr_small_begin_addr=small_addr,
            wr_small_max_addr=StdPara.small_max_addr,
            wr_small_min_addr=StdPara.small_min_addr,
            weight_begin_addr=0,
            bias_begin_addr=0,
            quant_q_in=0,
            quant_q_out=0
        )

        when(state===(num_stages).U){
            paras.set_maxp_reader1(reader1)
            paras.set_maxp_reader2(reader2)
            paras.set_write(writer)
            paras.set_maxpool_switch(read_switch)
            counter.set((in_chan*w*h/4-1).U)

            state := (num_stages+1).U
        }
        num_stages += 1
        when(state===(num_stages).U){
            when(writer.valid_out){
                when(counter.inc()){
                    state := (num_stages+1).U
                }
            }
            reader1.valid_in := true.B
            reader2.valid_in := true.B
        }
        num_stages += 1
    }

    def UPS(w: Int, h: Int, in_chan: Int): Unit = {
        val paras = GenAllPara(addr_w=StdPara.addr_w, h_w=StdPara.h_w, c_w=StdPara.c_w, id_w=StdPara.id_w, big_w=StdPara.big_w,
            width=w,
            height=h,
            wr_width=w*2,
            wr_height=h*2,
            in_chan=in_chan,
            out_chan=in_chan,
            para_num=StdPara.para_num,
            mask=StdPara.mask,
            big_begin_addr=big_addr,
            big_max_addr=StdPara.big_max_addr,
            big_min_addr=StdPara.big_min_addr,
            wr_big_begin_addr=nxt_big(big_addr+in_chan*w*h/2),
            wr_big_max_addr=StdPara.big_max_addr,
            wr_big_min_addr=StdPara.big_min_addr,
            small_begin_addr=small_addr,
            small_max_addr=StdPara.small_max_addr,
            small_min_addr=StdPara.small_min_addr,
            wr_small_begin_addr=nxt_small(small_addr+in_chan*w*h/4),
            wr_small_max_addr=StdPara.small_max_addr,
            wr_small_min_addr=StdPara.small_min_addr,
            weight_begin_addr=0,
            bias_begin_addr=0,
            quant_q_in=0,
            quant_q_out=0
        )

        big_addr = nxt_big(big_addr+in_chan*w*h/2)
        small_addr = nxt_small(small_addr+in_chan*w*h/4)

        when(state===(num_stages).U){
            paras.set_ups_reader1(reader1)
            paras.set_ups_reader2(reader2)
            paras.set_ups_read_pack(read_pack)
            paras.set_write(writer)
            paras.set_ups_switch(read_switch)
            counter.set((in_chan*w*h*4-1).U)

            state := (num_stages+1).U
        }
        num_stages += 1
        when(state===(num_stages).U){
            when(writer.valid_out){
                when(counter.inc()){
                    state := (num_stages+1).U
                }
            }
            reader1.valid_in := true.B
            reader2.valid_in := true.B
        }
        num_stages += 1
    }

    def LAST_PRINT(w: Int, h: Int, in_chan: Int): Unit = {
        val lst_counter = RegInit(0.U.asTypeOf(ACounter(16.W)))
        val lst_64_counter = RegInit(0.U.asTypeOf(ACounter(16.W)))
        val paras_print = GenAllPara(StdPara.addr_w, StdPara.h_w, StdPara.c_w, StdPara.id_w, big_w=StdPara.big_w,
            w, h, w, h,
            in_chan, 1,
            1, 0,
            big_addr, StdPara.big_max_addr, StdPara.big_min_addr,
            // StdPara.big_conv1_addr, 899, 0,
            0, 0, 0,
            small_addr, StdPara.small_max_addr, StdPara.small_min_addr,
            // StdPara.small_conv1_addr, 449, 0,
            0, 0, 0,
            0, 0, 0, 0
        )
        when(state===(num_stages).U){
            state := (num_stages+1).U
            paras_print.set_conv_reader1(reader1)
            paras_print.set_conv_reader2(reader2)
            paras_print.set_conv_read_pack(read_pack)
            read_switch.flag_job := true.B
            read_switch.job := ReadSwitchType.idle
            lst_64_counter.set(63.U)
            lst_counter.set((w*h*in_chan-1).U)
        }
        when(state===(num_stages+1).U){
            reader1.valid_in := true.B
            reader2.valid_in := true.B
            state := (num_stages+2).U
        }
        when(state===(num_stages+2).U){
            state := (num_stages+3).U
        }
        when(state===(num_stages+3).U){
            when(lst_64_counter.inc()){
                when(lst_counter.inc()){
                    state := (num_stages+4).U
                }
            }.elsewhen(lst_64_counter.ccnt===62.U){
                reader1.valid_in := true.B
                reader2.valid_in := true.B
            }
            when(read_pack.valid_out){
                io.complete := true.B
                io.valid := true.B
                io.output := read_pack.output.mat(0)
                for(i <- 0 to 62)
                    output_cache(i) := read_pack.output.mat(i+1)
            }.otherwise{
                io.valid := true.B
                io.output := output_cache(0)
                for(i <- 0 to 62)
                    output_cache(i) := output_cache(i+1)
            }
        }
        when(state===(num_stages+4).U){
            state := (num_stages+4).U
            io.complete_output := true.B
        }
        num_stages += 5
    }

    def CHECK_PRINT(): Unit = {
        when(io.valid){
            checksum := checksum^io.output.asUInt
        }
        io.checksum := checksum
    }
}

val rom_bias = if(StdPara.para_num==4) Module(new ROMBias(StdPara.para_num)).io else Module(new ROMHalfBias(StdPara.para_num)).io


package final_project

import chisel3._
import chisel3.util._

import chisel3.experimental.BundleLiterals._

class Wrapper extends Module{
    val io = IO(new Bundle{
        val raw_bram_addr = Output(UInt(32.W))
        val rgb_bram_wr_en = Output(Bool())
        val rgb_bram_addr = Output(UInt(12.W))
        val rgb_bram_data = Output(UInt(24.W))
        val raw_bram_data = Input(UInt(32.W))
    })

    val quant_in = 4

    val top = Module(new FinalTop).io
    val sigmoid = Module(new Sigmoid).io
    sigmoid.valid_in := top.valid
    sigmoid.input := top.output

    top.input := 0.S
    top.valid_in := false.B
    top.start := true.B

    io.raw_bram_addr := 0.U
    io.rgb_bram_wr_en := false.B
    io.rgb_bram_addr := 0.U
    io.rgb_bram_data := 0.U

    val in_state = RegInit(0.U(2.W))
    val raw_addr = RegInit(0.U(20.W))
    val raw_addr_0 = RegInit(false.B)
    val in_w = RegInit(0.U.asTypeOf(ACounter(2.W)))
    val in_h = RegInit(0.U.asTypeOf(ACounter(2.W)))
    val in_chan = RegInit(0.U.asTypeOf(ACounter(2.W)))
    val x = RegInit(0.U.asTypeOf(ACounter(3.W)))
    val y = RegInit(0.U.asTypeOf(ACounter(3.W)))

    switch(in_state){
        is(0.U){
            when(top.complete){
                in_state := 1.U
            }
            io.raw_bram_addr := 0.U
            in_w.set(3.U)
            in_h.set(3.U)
            in_chan.set(3.U)
            x.set(7.U)
            y.set(2.U, 7.U)
            // raw_addr := Cat(in_chan.ccnt, y.ccnt, in_h.ccnt, x.ccnt, in_w.ccnt)
            raw_addr := 4.U
            raw_addr_0 := false.B
        }
        is(1.U){
            when(y.inc()){
                when(x.inc()){
                    when(in_chan.inc()){
                        when(in_w.inc()){
                            when(in_h.inc()){
                                in_state := 2.U
                            }
                        }
                    }
                }
            }
            // raw_addr := ((in_w.ccnt*8+x.ccnt)*32+(in_h.ccnt*8+y.ccnt))*2+(in_chan.ccnt>>1.U)
            // raw_addr := Cat(in_chan.ccnt, y.ccnt, in_h.ccnt, x.ccnt, in_w.ccnt)
            raw_addr := Cat(in_w.ccnt, x.ccnt, in_h.ccnt, y.ccnt, in_chan.ccnt)
            raw_addr_0 := raw_addr(0)
            io.raw_bram_addr := (raw_addr >> 1.U) << 2.U // (AXI32)
            top.input := Cat(0.U(1.W), Mux(raw_addr_0, io.raw_bram_data(7, 0).asUInt, io.raw_bram_data(15, 8).asUInt) << quant_in.U).asSInt
            top.valid_in := true.B
        }
        is(2.U){
            io.raw_bram_addr := (raw_addr >> 1.U) << 2.U
            top.input := Cat(0.U(1.W), Mux(raw_addr_0, io.raw_bram_data(7, 0).asUInt, io.raw_bram_data(15, 8).asUInt) << quant_in.U).asSInt
            top.valid_in := true.B
            when(top.complete_output){
                in_state := 0.U
            }.otherwise{
                in_state := 3.U
            }
        }
        is(3.U){
            when(top.complete_output){
                in_state := 0.U
            }
        }
    }

    val out_state = RegInit(0.U(3.W))
    val out_reg = RegInit(VecInit(Seq.fill(64)(0.U(24.W))))
    val out_64_counter = RegInit(0.U.asTypeOf(ACounter(6.W)))

    val out_w = RegInit(0.U.asTypeOf(ACounter(3.W)))
    val out_h = RegInit(0.U.asTypeOf(ACounter(3.W)))
    val out_x = RegInit(0.U.asTypeOf(ACounter(3.W)))
    val out_y = RegInit(0.U.asTypeOf(ACounter(3.W)))
    for(i <- 0 to 62)
        out_reg(i) := out_reg(i+1)

    switch(out_state){
        is(0.U){
            when(sigmoid.valid_out){
                out_state := 1.U
            }
            out_reg(63) := Cat(sigmoid.output, 0.U(16.W))
            out_w.set(7.U)
            out_h.set(7.U)
            out_x.set(7.U)
            out_y.set(7.U)
            out_64_counter.set(1.U, 63.U)
        }
        is(6.U){
            when(sigmoid.valid_out){
                out_state := 1.U
            }.otherwise{
                out_state := 0.U
            }
            out_reg(63) := Cat(sigmoid.output, 0.U(16.W))
            out_64_counter.set(1.U, 63.U)
        }
        is(1.U){
            when(out_64_counter.inc()){
                out_state := 2.U
            }
            out_reg(63) := Cat(sigmoid.output, 0.U(16.W))
        }
        is(2.U){
            when(out_64_counter.inc()){
                out_state := 3.U
            }
            out_reg(63) := Cat(out_reg(0)(23, 16), sigmoid.output, 0.U(8.W))
        }
        is(3.U){

        is(5.U){
            when(out_y.inc()){
                when(out_x.inc()){
                    out_state := 6.U
                    when(out_h.inc()){
                        out_w.inc()
                    }
                }
            }
            // io.rgb_bram_addr := Cat(out_y.ccnt, out_h.ccnt, out_x.ccnt, out_w.ccnt)
            io.rgb_bram_addr := Cat(out_h.ccnt, out_x.ccnt, out_w.ccnt, out_y.ccnt)
            io.rgb_bram_data := out_reg(0)
            io.rgb_bram_wr_en := true.B
        }
    }

}

            when(out_64_counter.inc()){
                out_state := 5.U
            }


package final_project

import chisel3._
import chisel3.experimental.ChiselEnum

class GenAllPara(val addr_w: Int, val h_w: Int, val c_w: Int, val id_w: Int, big_w: Int){
    var width = 0
    var height = 0
    var wr_width = 0
    var wr_height = 0
    var in_chan = 0
    var out_chan = 0
    var bank_id_big = Array[Int]()
    var bank_id_small = Array[Array[Int]]()
    var para_num = 0
    var mask = 0
    var big_begin_addr = 0
    var big_max_addr = 0
    var big_min_addr = 0
    var wr_big_begin_addr = 0
    var wr_big_max_addr = 0
    var wr_big_min_addr = 0
    var small_begin_addr = 0
    var small_max_addr = 0
    var small_min_addr = 0
    var wr_small_begin_addr = 0
    var wr_small_max_addr = 0
    var wr_small_min_addr = 0
    var weight_begin_addr = 0
    var bias_begin_addr = 0
    var quant_q_in = 0
    var quant_q_out = 0
    def set_conv_reader1(reader1: GraphReaderBundle): Bundle = {
        val (loop_num, loop_h) = (2*out_chan/para_num, 1)
        val begin_loop_num1 = out_chan/para_num
        reader1.flag_job := true.B
        reader1.job_type := ReadType.toConvOrCopy

        reader1.job := GenConvReadJob.gen(
            addr_w, h_w, c_w, id_w, big_w,
            loop_num, begin_loop_num1, loop_h,
            height/2, width, in_chan,
            big_begin_addr, big_min_addr, big_max_addr,
            small_begin_addr, small_min_addr, small_max_addr,
            bank_id_big(0), bank_id_small(0)
        )

        return reader1
    }

    def set_conv_reader2(reader2: GraphReaderBundle): Bundle = {
        val (loop_num, loop_h) = (2*out_chan/para_num, 1)
        val begin_loop_num2 = 0

        reader2.flag_job := true.B
        reader2.job_type := ReadType.toConvOrCopy

        reader2.job := GenConvReadJob.gen(
            addr_w, h_w, c_w, id_w, big_w,
            loop_num, begin_loop_num2, loop_h,
            height/2, width, in_chan,
            big_begin_addr, big_min_addr, big_max_addr,
            small_begin_addr, small_min_addr, small_max_addr,
            bank_id_big(1), bank_id_small(1)
        )

        return reader2
    }

    def set_copy_reader1(reader1: GraphReaderBundle, copy_cnt: Int): Bundle = {
        val (loop_num, loop_h) = (2, copy_cnt)
        val begin_loop_num1 = 1
        reader1.flag_job := true.B
        reader1.job_type := ReadType.toConvOrCopy

        reader1.job := GenConvReadJob.gen(
            addr_w, h_w, c_w, id_w, big_w,
            loop_num, begin_loop_num1, loop_h,
            height/2, width, in_chan,
            big_begin_addr, big_min_addr, big_max_addr,
            small_begin_addr, small_min_addr, small_max_addr,
            bank_id_big(0), bank_id_small(0)
        )

        return reader1
    }

    def set_copy_reader2(reader2: GraphReaderBundle, copy_cnt: Int): Bundle = {
        val (loop_num, loop_h) = (2, copy_cnt)
        val begin_loop_num2 = 0

        reader2.flag_job := true.B
        reader2.job_type := ReadType.toConvOrCopy

        reader2.job := GenConvReadJob.gen(
            addr_w, h_w, c_w, id_w, big_w,
            loop_num, begin_loop_num2, loop_h,
            height/2, width, in_chan,
            big_begin_addr, big_min_addr, big_max_addr,
            small_begin_addr, small_min_addr, small_max_addr,
            bank_id_big(1), bank_id_small(1)
        )

        return reader2
    }

    def set_ups_reader1(reader1: GraphReaderBundle): Bundle = {
        val (loop_num, loop_h) = (4, 2)
        val begin_loop_num1 = 2
        reader1.flag_job := true.B
        reader1.job_type := ReadType.toConvOrCopy

        reader1.job := GenConvReadJob.gen(
            addr_w, h_w, c_w, id_w, big_w,
            loop_num, begin_loop_num1, loop_h,
            height/2, width, in_chan,
            big_begin_addr, big_min_addr, big_max_addr,
            small_begin_addr, small_min_addr, small_max_addr,
            bank_id_big(0), bank_id_small(0)
        )

        return reader1
    }

    def set_ups_reader2(reader2: GraphReaderBundle): Bundle = {
        val (loop_num, loop_h) = (4, 2)
        val begin_loop_num2 = 0

        reader2.flag_job := true.B
        reader2.job_type := ReadType.toConvOrCopy

        reader2.job := GenConvReadJob.gen(
            addr_w, h_w, c_w, id_w, big_w,
            loop_num, begin_loop_num2, loop_h,
            height/2, width, in_chan,
            big_begin_addr, big_min_addr, big_max_addr,
            small_begin_addr, small_min_addr, small_max_addr,
            bank_id_big(1), bank_id_small(1)
        )

        return reader2
    }

    def set_copy_reader1_job2(reader1: GraphReaderBundle, copy_cnt: Int): Bundle = {
        val (loop_num, loop_h) = (2, copy_cnt)
        val begin_loop_num1 = 1

        reader1.job2 := GenConvReadJob.gen(
            addr_w, h_w, c_w, id_w, big_w,
            loop_num, begin_loop_num1, loop_h,
            height/2, width, in_chan,
            big_begin_addr, big_min_addr, big_max_addr,
            small_begin_addr, small_min_addr, small_max_addr,
            bank_id_big(0), bank_id_small(0)
        )

        return reader1
    }

    def set_copy_reader2_job2(reader2: GraphReaderBundle, copy_cnt: Int): Bundle = {
        val (loop_num, loop_h) = (2, copy_cnt)
        val begin_loop_num2 = 0

        reader2.job2 := GenConvReadJob.gen(
            addr_w, h_w, c_w, id_w, big_w,
            loop_num, begin_loop_num2, loop_h,
            height/2, width, in_chan,
            big_begin_addr, big_min_addr, big_max_addr,
            small_begin_addr, small_min_addr, small_max_addr,
            bank_id_big(1), bank_id_small(1)
        )

        return reader2
    }

    def set_maxp_reader1(reader1: GraphReaderBundle): Bundle = {
        reader1.flag_job := true.B
        reader1.job_type := ReadType.toMaxp
        val bank_id_small0 = Array[Int](bank_id_small(0)(3), bank_id_small(0)(2), bank_id_small(0)(1), bank_id_small(0)(0))

        reader1.job := GenMaxpReadJob.gen(
            addr_w, h_w, c_w, id_w, big_w,
            1, 1, 1,
            height/2, width, in_chan,
            big_begin_addr, big_min_addr, big_max_addr,
            small_begin_addr, small_min_addr, small_max_addr,
            bank_id_big(0), bank_id_small0
        )

        return reader1
    }

    def set_maxp_reader2(reader2: GraphReaderBundle): Bundle = {

        reader2.flag_job := true.B
        reader2.job_type := ReadType.toMaxp
        val bank_id_small1 = Array[Int](bank_id_small(1)(3), bank_id_small(1)(2), bank_id_small(1)(1), bank_id_small(1)(0))

        reader2.job := GenMaxpReadJob.gen(
            addr_w, h_w, c_w, id_w, big_w,
            1, 1, 1,
            height/2, width, in_chan,
            big_begin_addr, big_min_addr, big_max_addr,
            small_begin_addr, small_min_addr, small_max_addr,
            bank_id_big(1), bank_id_small1
        )

        return reader2
    }

    def set_read_weight(rw: WeightReaderBundle): Bundle = {
        rw.flag_job := true.B
        val weight_end_addr = weight_begin_addr+in_chan*out_chan/para_num-1
        rw.begin_addr := weight_begin_addr.U
        rw.addr_end := weight_end_addr.U
        return rw
    }

    def set_conv_read_pack(rp: PackReadDataBundle): Bundle = {
        rp.flag_job := true.B
        rp.job := GenPackJob.gen(addr_w, h_w, c_w, big_w,  width-1, height-1, in_chan*out_chan/para_num-1, in_chan-1)
        return rp
    }

    def set_copy_read_pack(rp: PackReadDataBundle): Bundle = {
        rp.flag_job := true.B
        rp.job := GenPackJob.gen(addr_w, h_w, c_w, big_w,  width-1, height-1, in_chan-1, in_chan-1)
        return rp
    }

    def set_ups_read_pack(rp: PackReadDataBundle): Bundle = {
        rp.flag_job := true.B
        rp.job := GenPackJob.gen(addr_w, h_w, c_w, big_w,  width-1, height-1, in_chan*2-1, in_chan-1)
        return rp
    }

    def set_conv_read_switch(rs: ReadSwitchBundle): Bundle = {
        rs.flag_job := true.B
        rs.job := ReadSwitchType.toConv
        return rs
    }

    def set_maxpool_switch(rs: ReadSwitchBundle): Bundle = {
        rs.flag_job := true.B
        rs.job := ReadSwitchType.toMaxp
        return rs
    }

    def set_copy_switch(rs: ReadSwitchBundle): Bundle = {
        rs.flag_job := true.B
        rs.job := ReadSwitchType.toCopy
        return rs
    }

    def set_mult_switch(rs: ReadSwitchBundle): Bundle = {
        rs.flag_job := true.B
        rs.job := ReadSwitchType.toMult2
        return rs
    }

    def set_ups_switch(rs: ReadSwitchBundle): Bundle = {
        rs.flag_job := true.B
        rs.job := ReadSwitchType.toUps
        rs.in_h := (height*2-1).U
        rs.in_chan := (in_chan-1).U
        return rs
    }

    def set_conv_accu(ac: AccumuBundle): Bundle = {
        ac.flag_job := true.B
        ac.csum := (in_chan-1).U
        ac.is_in_use := true.B
        val bias_end_addr = bias_begin_addr+out_chan/para_num-1
        ac.bias_begin_addr := bias_begin_addr.U
        ac.bias_end_addr := bias_end_addr.U
        return ac
    }

    def set_unuse_accu(ac: AccumuBundle): Bundle = {
        ac.flag_job := true.B
        ac.is_in_use := false.B
        return ac
    }

    def set_quant(q: QuantBundle): Bundle = {
        q.flag_job := true.B
        q.quant_in := (quant_q_in-quant_q_out).U
        return q
    }

    def set_write_alar(wr: RealWriterBundle, al: Int, ar: Int): Bundle = {
        wr.flag_job := true.B

        val bank_id_small0 = Array[Int](bank_id_small(0)(1), bank_id_small(0)(2), bank_id_small(0)(3), bank_id_small(0)(0))
        val bank_id_small1 = Array[Int](bank_id_small(1)(1), bank_id_small(1)(2), bank_id_small(1)(3), bank_id_small(1)(0))
        wr.job.job(0) := GenWriteConvJob.gen(
            addr_w, h_w, c_w, id_w,
            wr_height/2, wr_width, out_chan,
            wr_big_begin_addr, wr_big_min_addr, wr_big_max_addr,
            wr_small_begin_addr, wr_small_min_addr, wr_small_max_addr,
            bank_id_big(0), bank_id_small0, al, ar
        )
        wr.job.job(1) := GenWriteConvJob.gen(
            addr_w, h_w, c_w, id_w,
            wr_height/2, wr_width, out_chan,
            wr_big_begin_addr, wr_big_min_addr, wr_big_max_addr,
            wr_small_begin_addr, wr_small_min_addr, wr_small_max_addr,
            bank_id_big(1), bank_id_small1, al, ar
        )
        wr.job.out_chan := (ar-al).U
        return wr
    }

    def set_write(wr: RealWriterBundle): Bundle = {
        return set_write_alar(wr, 0, out_chan-1)
    }
}

object GenAllPara{
    def apply(
        addr_w: Int,
        h_w: Int,
        c_w: Int,
        id_w: Int,
        big_w: Int,
        width: Int,
        height: Int,
        wr_width: Int,
        wr_height: Int,
        in_chan: Int,
        out_chan: Int,
        para_num: Int,
        mask: Int,
        big_begin_addr: Int,
        big_max_addr: Int,
        big_min_addr: Int,
        wr_big_begin_addr: Int,
        wr_big_max_addr: Int,
        wr_big_min_addr: Int,
        small_begin_addr: Int,
        small_max_addr: Int,
        small_min_addr: Int,
        wr_small_begin_addr: Int,
        wr_small_max_addr: Int,
        wr_small_min_addr: Int,
        weight_begin_addr: Int,
        bias_begin_addr: Int,
        quant_q_in: Int,
        quant_q_out: Int
    ): GenAllPara = {
        val ret = new GenAllPara(addr_w, h_w, c_w, id_w, big_w)
        ret.width = width
        ret.height = height
        ret.wr_width = wr_width
        ret.wr_height = wr_height
        ret.in_chan = in_chan
        ret.out_chan = out_chan
        ret.bank_id_big = Array(1<<0, 1<<1)
        ret.bank_id_small = Array(Array(1<<3, 1<<0, 1<<1, 1<<2), Array(1<<7, 1<<4, 1<<5, 1<<6))
        ret.para_num = para_num
        ret.big_begin_addr = big_begin_addr
        ret.big_max_addr = big_max_addr
        ret.big_min_addr = big_min_addr
        ret.wr_big_begin_addr = wr_big_begin_addr
        ret.wr_big_max_addr = wr_big_max_addr
        ret.wr_big_min_addr = wr_big_min_addr
        ret.small_begin_addr = small_begin_addr
        ret.small_max_addr = small_max_addr
        ret.small_min_addr = small_min_addr
        ret.wr_small_begin_addr = wr_small_begin_addr
        ret.wr_small_max_addr = wr_small_max_addr
        ret.wr_small_min_addr = wr_small_min_addr
        ret.weight_begin_addr = weight_begin_addr
        ret.bias_begin_addr = bias_begin_addr
        ret.quant_q_in = quant_q_in
        ret.quant_q_out = quant_q_out
        ret.mask = mask
        return ret
    }
}

object StdPara{
    val w = 16
    val big_bank_w = 768
    val big_bank_addr_w = 10
    val big_global_size = 1024
    val small_bank_w = 128
    val small_bank_addr_w = 9
    val small_global_size = 512
    val bias_w = 36
    val weight_w = 144
    val bias_addr_w = 8
    val weight_addr_w = 13
    val big_w = 16
    val h_w = 3
    val addr_w = 10
    val c_w = 6
    val id_w = 8
    val para_num = 2
    val mask = (1<<para_num)-1
    val rd_para_num = 4
    val dsp_w = 18

    val big_global_addr = big_global_size-1
    val small_global_addr = small_global_size-1
    val big_max_addr = big_global_size-128-256-1
    val small_max_addr = small_global_size-64-128-1
    val big_conv0_addr = big_global_size-128
    val big_conv1_addr = big_global_size-128-256
    val small_conv0_addr = small_global_size-64
    val small_conv1_addr = small_global_size-64-128
    val big_min_addr = 0
    val small_min_addr = 0
}

val _min = (x: Int, y: Int) => (if(x>=y) y else x)


package final_project

import chisel3._
import chisel3.experimental.ChiselEnum

class TransBundle extends Bundle{
    val valid_in = Input(Bool())
    val valid_out = Output(Bool())
    val flag_job = Input(Bool())
}

class ACounter(val w: chisel3.internal.firrtl.Width) extends Bundle{
    val ccnt = UInt(w)
    val cend = UInt(w)
    def inc(): Bool = {
        val nxt = Wire(Bool())
        nxt := ccnt===cend
        ccnt := Mux(nxt, 0.U, ccnt+1.U)
        return nxt
    }
    def set(csum: UInt): Unit = {
        cend := csum
        ccnt := 0.U
    }
    def set(begin: UInt, csum: UInt): Unit = {
        cend := csum
        ccnt := begin
    }
    def clr(): Unit = {
        ccnt := 0.U
        cend := 0.U
    }
}

object ACounter{

    def apply(w: chisel3.internal.firrtl.Width) = new ACounter(w)
}

class RCounter(val w: chisel3.internal.firrtl.Width) extends Bundle{
    val ccnt = UInt(w)
    val cr = UInt(w)
    val cl = UInt(w)
    def inc(): Bool = {
        val nxt = Wire(Bool())
        nxt := ccnt===cr
        ccnt := Mux(nxt, cl, ccnt+1.U)
        return nxt
    }
    def set(r: UInt): Unit = {
        cr := r
        cl := 0.U
        ccnt := 0.U
    }
    def set(l: UInt, r: UInt): Unit = {
        cr := r
        cl := l
        ccnt := l
    }
}

object RCounter{
    def apply(w: Int) = new RCounter(w.W)
    def apply(w: chisel3.internal.firrtl.Width) = new RCounter(w)
}


package final_project

import chisel3._
import chisel3.util._

class Address(val addr_w: Int, val id_w: Int) extends Bundle{
    val addr = UInt(addr_w.W)
    val bank_id = UInt(id_w.W)
}

object Address{

    def apply(addr: Data, id: Data): Data = {
        val ret = Wire(Address(addr.getWidth, id.getWidth))
        ret.addr := addr
        ret.bank_id := id
        return ret
    }
}

class AddressReadGroup(val addr_w: Int, val id_w: Int) extends Bundle{
    val addrs = Vec(5, Address(addr_w, id_w))
}

class AddressWriteGroup(val addr_w: Int, val id_w: Int) extends Bundle{
    val addrs = Vec(3, Address(addr_w, id_w))
}

trait GenAddressInterface{
    def go(): Unit
    def output(): Data
}

// remember this is a register, not wire
abstract class GenAddress(addr_w: Int, h_w: Int, c_w: Int, id_w: Int) extends Bundle with GenAddressInterface{
    val max_addr = UInt(addr_w.W)
    val min_addr = UInt(addr_w.W)
    val dta_addr = UInt(addr_w.W)
    val now_addr = UInt(addr_w.W)
    val cnt_ic = ACounter(c_w.W)
    val cnt_x = ACounter(h_w.W)
    val cnt_y = ACounter(h_w.W)
    val bank_id = UInt(id_w.W)
    val block_size = UInt(addr_w.W)
    val column_size = UInt(addr_w.W)
    def clamp(addr: UInt): UInt = {
        val nxt = Wire(UInt(addr.getWidth.W))
        when(addr>max_addr){
            nxt := addr+dta_addr
        }.otherwise{
            nxt := addr
        }
        return nxt
    }
    def set(x: WRJob): Unit = {
        max_addr := x.max_addr
        min_addr := x.min_addr
        now_addr := x.begin_addr
        block_size := x.block_size
        column_size := x.column_size
        cnt_ic.set(x.cnt_ic_end)
        cnt_x.set(x.cnt_x_end)
        cnt_y.set(x.cnt_y_end)
        bank_id := x.bank_id
        dta_addr := -x.max_addr-1.U+x.min_addr
    }
    override def output(): Data = {
        return Address(now_addr, bank_id)
    }
}

class GenReadBigBankAddress(addr_w: Int, h_w: Int, c_w: Int, id_w: Int) extends GenAddress(addr_w, h_w, c_w, id_w){
    val cnt_loop = ACounter((2*c_w).W)
    val cnt_ups = ACounter(1.W)
    val cnt_maxp = ACounter(1.W)
    val y_begin_addr = UInt(addr_w.W)
    val ic_begin_addr = UInt(addr_w.W)
    val flag_end = Bool()
    override def go(): Unit = {
        val nxt_addr = Wire(UInt(addr_w.W))
        nxt_addr := 0.U
        when(cnt_ic.inc()){
            when(cnt_loop.inc()){
                when(cnt_y.inc()){
                    when(cnt_ups.inc()){
                        when(cnt_x.inc()){
                            flag_end := true.B
                        }.otherwise{
                            nxt_addr := now_addr+1.U
                        }
                        y_begin_addr := clamp(nxt_addr)
                    }.otherwise{
                        nxt_addr := y_begin_addr
                    }
                }.otherwise{
                    nxt_addr := now_addr+1.U
                }
                ic_begin_addr := clamp(nxt_addr)
            }.otherwise{
                nxt_addr := ic_begin_addr
            }
        }.otherwise{
            nxt_addr := now_addr+1.U
        }
        now_addr := clamp(nxt_addr)
    }
    def go_maxp(): Unit = {
        val nxt_addr = Wire(UInt(addr_w.W))
        nxt_addr := 0.U
        when(cnt_maxp.inc()){
            when(cnt_ic.inc()){
                when(cnt_y.inc()){
                    when(cnt_x.inc()){
                        flag_end := true.B
                    }.otherwise{
                        nxt_addr := now_addr+1.U
                    }
                }.otherwise{
                    nxt_addr := y_begin_addr+1.U
                }
            }.otherwise{
                nxt_addr := y_begin_addr+1.U
            }
            y_begin_addr := clamp(nxt_addr)
        }.otherwise{
            nxt_addr := y_begin_addr+column_size
        }
        now_addr := clamp(nxt_addr)
    }
    def is_end(): Bool = {
        return flag_end
    }
    def set(x: ReadBigJob): Unit = {
        super.set(x)
        flag_end := false.B
        cnt_loop.set(x.begin_loop, x.cnt_loop_end)
        cnt_ups.set(x.cnt_ups_end)
        cnt_maxp.set(1.U)
        y_begin_addr := x.begin_addr
        ic_begin_addr := x.begin_addr
    }
}

class GenReadSmallBankAddress(addr_w: Int, h_w: Int, c_w: Int, id_w: Int, big_w: Int) extends GenAddress(addr_w, h_w, c_w, id_w){
    val ano_bank_id = UInt(id_w.W)
    val cnt_invalid = ACounter(big_w.W)
    val cnt_maxp = ACounter(1.W)
    val cnt_ups = ACounter(1.W)
    val cnt_loop = ACounter((2*c_w).W)
    val cnt_swap = ACounter(1.W)
    val y_begin_addr = UInt(addr_w.W)
    val ic_begin_addr = UInt(addr_w.W)
    val start = Bool()
    override def go(): Unit = {
        val nxt_addr = Wire(UInt(addr_w.W))
        nxt_addr := 0.U
        when(cnt_invalid.inc()||start){
            start := true.B
            when(cnt_ic.inc()){
                when(cnt_loop.inc()){
                    when(cnt_y.inc()){
                        when(cnt_ups.inc()){
                            when(cnt_swap.inc()){
                                nxt_addr := now_addr+1.U
                                y_begin_addr := clamp(nxt_addr)
                            }.otherwise{
                                nxt_addr := y_begin_addr
                            }
                            bank_id := ano_bank_id
                            ano_bank_id := bank_id
                        }.otherwise{
                            nxt_addr := y_begin_addr
                        }
                    }.otherwise{
                        nxt_addr := now_addr+1.U
                    }
                    ic_begin_addr := clamp(nxt_addr)
                }.otherwise{
                    nxt_addr := ic_begin_addr
                }
            }.otherwise{
                nxt_addr := now_addr+1.U
            }
            now_addr := clamp(nxt_addr)
        }
    }
    def go_maxp(): Unit = {
        val nxt_addr = Wire(UInt(addr_w.W))
        nxt_addr := 0.U
        when(cnt_maxp.inc()){
            now_addr := clamp(now_addr+1.U)
        }
        bank_id := ano_bank_id
        ano_bank_id := bank_id
    }
    def set(x: ReadSmallJob): Unit = {
        super.set(x)
        ano_bank_id := x.ano_bank_id
        cnt_loop.set(x.begin_loop, x.cnt_loop_end)
        cnt_invalid.set(x.cnt_invalid_end)
        cnt_ups.set(x.cnt_ups_end)
        cnt_maxp.set(1.U)
        cnt_swap.set(x.begin_swap, 1.U)
        start := false.B
        y_begin_addr := x.begin_addr
        ic_begin_addr := x.begin_addr
    }
}

class GenWriteSmallBankAddress(addr_w: Int, h_w: Int, c_w: Int, id_w: Int) extends GenAddress(addr_w, h_w, c_w, id_w){
    // a is the output_chan that it outputs,
    val a = UInt(c_w.W)
    val ano_bank_id = UInt(id_w.W)
    val cnt_swap = Bool()
    val y_begin_addr = UInt(addr_w.W)
    override def go(): Unit = {
        val nxt_addr = Wire(UInt((addr_w+1).W))
        nxt_addr := 0.U
        when(cnt_ic.inc()){
            when(cnt_y.inc()){
                cnt_swap := ~cnt_swap
                when(cnt_swap){
                    nxt_addr := now_addr+a
                    y_begin_addr := clamp(nxt_addr)
                }.otherwise{
                    nxt_addr := y_begin_addr
                }
                bank_id := ano_bank_id
                ano_bank_id := bank_id
            }.otherwise{
                nxt_addr := now_addr+&a
            }
        }.otherwise{
            nxt_addr := now_addr+&1.U
        }
        now_addr := clamp(nxt_addr)
    }

    def set(x: WriteSmallJob): Unit = {
        super.set(x)
        a := x.a
        ano_bank_id := x.ano_bank_id
        y_begin_addr := x.begin_addr
        cnt_swap := false.B
    }
}

class GenWriteBigBankAddress(addr_w: Int, h_w: Int, c_w: Int, id_w: Int) extends GenAddress(addr_w, h_w, c_w, id_w){
    val a = UInt(c_w.W)
    override def go(): Unit = {
        val nxt_addr = Wire(UInt((addr_w+1).W))
        when(cnt_ic.inc()){
            nxt_addr := now_addr+&a
        }.otherwise{
            nxt_addr := now_addr+&1.U
        }
        now_addr := clamp(nxt_addr)
    }

    def set(x: WriteBigJob): Unit = {
        super.set(x)
        a := x.a
    }

}

class ReadGroup(addr_w: Int, h_w: Int, c_w: Int, id_w: Int, big_w: Int) extends Bundle{
    val big = new GenReadBigBankAddress(addr_w, h_w, c_w, id_w)
    val small = Vec(4, new GenReadSmallBankAddress(addr_w, h_w, c_w, id_w, big_w))
    def set(x: ReadJobs): Unit = {
        big.set(x.big)
        for(i <- 0 to 3)
            small(i).set(x.small(i))
    }
    def go(): Unit = {
        big.go()
        for(i <- 0 to 3)
            small(i).go()
    }
    def go_maxp(): Unit = {
        big.go_maxp()
        for(i <- 0 to 1)
            small(i).go_maxp()
    }
    def output(): Data = {
        val ret = Wire(new AddressReadGroup(addr_w, id_w))
        ret.addrs(0) := big.output()
        for(j <- 0 to 3)
            ret.addrs(j+1) := small(j).output()
        return ret
    }
    def is_end(): Bool = {
        return big.is_end()
    }
}

class WriteGroup(addr_w: Int, h_w: Int, c_w: Int, id_w: Int) extends Bundle{
    val big = new GenWriteBigBankAddress(addr_w, h_w, c_w, id_w)
    val small = Vec(2, new GenWriteSmallBankAddress(addr_w, h_w, c_w, id_w))
    def set(x: WriteJobs): Unit = {
        big.set(x.big)
        for(j <- 0 to 1)
            small(j).set(x.small(j))
    }
    def go(): Unit = {
        big.go()
        for(j <- 0 to 1)
            small(j).go()
    }
    def output(): Data = {
        val ret = Wire(new AddressWriteGroup(addr_w, id_w))
        ret.addrs(0) := big.output()
        for(j <- 0 to 1)
            ret.addrs(j+1) := small(j).output()
        return ret
    }
}

def apply(addr_w: Int, id_w: Int) = new Address(addr_w, id_w)


package final_project

import chisel3._
import chisel3.util._
import chisel3.experimental.ChiselEnum

object ReadType extends ChiselEnum{
    val toConvOrCopy = Value(0.U)         // Sigmoid, conv, mult
    val toMaxp = Value(1.U)              // Maxpool
}

class GraphReaderBundle(val addr_w: Int, val h_w: Int, val c_w: Int, val id_w: Int, val big_w: Int) extends TransBundle{
    val job = Input(new ReadJobs(addr_w, h_w, c_w, id_w, big_w))
    val job2 = Input(new ReadJobs(addr_w, h_w, c_w, id_w, big_w))
    val signal = Input(Bool())
    val job_type = Input(ReadType())
    val to_banks = Output(new AddressReadGroup(addr_w, id_w))
}

class GraphReader(val addr_w: Int, val h_w: Int, val c_w: Int, val id_w: Int, val big_w: Int) extends Module{
    val io = IO(new GraphReaderBundle(addr_w, h_w, c_w, id_w, big_w))

    io.valid_out := false.B
    io.to_banks := 0.U.asTypeOf(io.to_banks)

    val job_type = RegInit(ReadType.toConvOrCopy)

    val gen = RegInit(0.U.asTypeOf(Vec(2, new ReadGroup(addr_w, h_w, c_w, id_w, big_w))))

    val state = RegInit(0.U(1.W))

    when(io.flag_job){
        gen(0).set(io.job)
        gen(1).set(io.job2)
        state := 0.U
        job_type := io.job_type
    }.elsewhen(io.valid_in){
        state := state^io.signal.asUInt
        switch(job_type){
            is(ReadType.toConvOrCopy){
                gen(state).go()
            }

        io.to_banks := gen(state).output()
    }

}

            is(ReadType.toMaxp){
                gen(state).go_maxp()
            }