網頁

2019年11月28日 星期四

安裝 Fish shell


fish 是 friendly interactive shell 的縮寫,也是一個 Linux shell ,專注於互動應用、易用性和友好的使用者體驗,其設計目的是通過容易發現,記住和使用的方式為使用者提供一套豐富、強大的功能。fish 於2005年在GNU通用公眾授權條款條款下正式釋出,是一款自由軟體。

安裝 Fish shell:
輸入下面指令
sudo apt-add-repository ppa:fish-shell/release-3
sudo apt-get update
sudo apt-get install fish


設定 Fish shell 為預設 shell:

安裝完 Fish shell 後,預設啟用還是原本的 Bash,
可以嘗試使用這個指令,來把預設的 shell 切換成 Fish:
chsh -s /usr/bin/fish username
(需登出再登入才生效)

每次開啟終端機都會先出現歡迎字串:

    Welcome to fish, the friendly interactive shell
    Type help for instructions on how to use fish

如要關閉歡迎字串,編輯 ~/.config/fish/config.fish 檔案
加上 set fish_greeting 即可。

改變提示字串 prompt:
輸入 fish_config 會開啟預設瀏覽器來設定 fish 的基本功能

*如果執行 fish_config 出現:
distutils.spawn import find_executable

請安裝 python-distutils
sudo apt install python3-distutils -y




點選 prompt 功能,選擇一個 prompt 的種類,選好後按下 Set Prompt
我選了一個 simple_Pythonista 的樣式:

開啟終端機後提示字串就成為這樣:

改變 prompt 顏色:
當前的 prompt 設定檔位於 ~/.config/fish/functions/fish_prompt.fish
輸入 vim fish_prompt.fish,或是用文字編輯器開啟

#5~6 行:是設定使用者的提示字串顏色
#10~#11行:是設定 hostname 的提示字串顏色

基本色的 Color Table 如下:
可以輸入顏色名稱或是 16進制值來改變顏色

 延伸的顏色表如下:

我的 prompt 改成如下:
set_color FFCC00
printf '%s' (whoami)

set_color FF99FF 
echo -n (prompt_hostname)

原本的 pwd:
printf '%s' (prompt_pwd)
改為:
printf '%s' $PWD|sed 's|'$HOME'|~|'

顯示如下:
(修改前)
 (修改後)
 


這樣就可以自己動手打造一個屬如自己風格的提示字串。

 

2019年11月21日 星期四

Linux 的 PDF-Shuffler

PDF-Shuffler 是一個使用 python-gtk 寫成的小工具,他可以協助使用者合併或分割 PDF 檔,
另外也可以對 PDF 的每一頁做旋轉、切割或重新排序。事實上他就是 python-pyPdf 的一個
圖形化使用者介面。

在 Ubuntu Linux 下可以用 apt 直接安裝:

sudo apt-get install pdfshuffler

其使用者介面很簡單,只有幾個按鈕而已,使用者一開始可以使用 Import pdf 功能匯入要編輯的 PDF 檔,匯入之後 PDF-Shuffler 就會將 PDF 檔的內容顯示出來:


這個時候使用者就可以開始編輯了,若要調整每一頁的順序,可以直接使用滑鼠將要調整的
那一頁拖到想要的位置:


另一個常用的就是分割 PDF,
選擇要分割的頁面,將滑鼠移至 pdf 的頁面上按下滑鼠右鍵,選擇 Export selection


還有像是旋轉頁面,刪除頁面操作都很直觀簡單,有需要的人可以下載來試試。


2019年11月12日 星期二

Julia 字典 (Dictionary) 的應用

有次看到工程師在用別人做的 Excel巨集跑 data,這個資料是 wafer 測試完成後將需要做
Laser Trim 的座標檔匯入Excel,然後檢查是不是有重複座標,有時候一片wafer 少則幾千個 dies,多則幾萬個 dies,就算電腦等級再高跑起來也是很慢。
這時如果你是主管你該怎麼做? 該說: 好認真,辛苦了。還是: 笨死了,動作這麼慢!
這種都是典型的嘴砲主管,自己沒本事只會要求員工,我最唾棄這種人啦,偏偏老闆最喜歡
這樣的人。
好啦,上面都是廢話,今天要講的是 Julia 的內建型別:字典(Dictionary)。
字典和陣列很像,在陣列裡,索引必須是整數;但在字典裡幾乎可以是任何型別。


字典包含一組稱為鍵(key)的索引,以及一組值(value)。每個鍵都被賦予一個單一的值,就像上述例子wafer的座標是唯一的,查找是否重複這種工作就很適合用 dict 來解決。
首先,建立一個字典:
person = Dict()

將 "Peter" 這個 key 對應到值 25 存入字典:
 person["Peter"] = 25

keys函數可以傳回字典所有的鍵:
ks = keys(person)

也可以用 ∈ (輸入 \in Tab) 運算子來查看某個鍵是否出現在字典中:
"Peter" ∈ ks
true


知道了怎麼操作 Dict,現在來看一下實際的例子:

下圖左邊數字用逗號分隔是座標值,如 143,9:後面是測試資料

我將每行資料從檔案讀出後放入 array 變數中,建立一個 coord_dict
對 array 每一筆(每行)資料用正規式做判斷是否為資料行,
if occursin(r"(^\d*,\d*)(:.*$)", line)
如果是用 split 函數以 ":" 分割成座標和資料
(coord, data) = split(line, ":")
143,9:die_type=1 die_test=1 bin=1

將座標(143,9) 和值(:die_type=1 die_test=1 bin=1) 分別存入 coord,data變數

檢查 coord 是否出現在 coord_dict 字典中,如果是就印出座標與值;
如果沒有就放入字典中。

if coord ∈ keys(coord_dict)
    重複的座標資料
else
    coord_dict[coord] = data
end

程式碼如下:

我將一筆重複資料放入做測試:

執行的結果:

74024 筆資料中找出一筆重複資料,花費時間 0.138420 sec

這樣只要將重複資料刪除再存檔就可以,剩下時間就可以去喝杯咖啡了。
(之後將這程式放上 server,當測試完後自動檢查並匯出結果省去了人工檢查)

完整程式碼:
https://glot.io/snippets/fhrmy2ytzs


2019年11月9日 星期六

使用 Julia 讀取TSK map 檔案(二)

接著要讀取第237 byte 的資料一直到最後
先來看一下規格書是怎麼說的:
根據下圖每一個 die的測試結果存放在6個 bytes 資料裡面,

例如:第一個 byte 的14,15 bit是存放測試的結果,
0:表示沒測試1表示Pass die
2:表示Fail 1die3:表示Fail 2die
那要麼取出這個 bit 呢? 很簡單將這個 byte資料右移6次的結果就是第14,15 bit 的資料:
 die_test = Int(data[idx]) >> 6
 
 再來看另外一組,如第三個 byte 和第二個 byte 的第1個 bit是存放 Y座標值,要怎麼取出呢?
 第三個 byte全部是值只要將資料轉成數字,第二個 byte的第1 bit要取出只要對它和1做 and
運算即可:(因為是高位元所以要乘上 256)
die_y = (Int(data[idx+2]) & 1)*256 + Int(data[idx+3])

最後1個 byte (0-5 bit)是存放 bin 的結果:
bin_n = Int(data[idx + 5])

看你需要讀取哪些資料就用上述方式取出,
接著再建立一個2D的陣列存放資料,由於資料會轉成字串形式因此我宣告為字串型態:
map_txt = ones(String, row, col)

這裡要注意的是通常在C或是Python陣列都是如下的格式,要儲存(取出)都是行(col),列(row)
如下圖,取出 3 的資料會寫成 a = array[2,0] ,a = 3
但在 Julia 是先 列(row),再 行(col)

這樣就可以寫個代碼來表示一下流程:
map = ones(String, row, col)
idx = 237
for r = 1 to row
    for c = 1 to col
        map[r,c] = read_data
        idx += 6
    end
end

執行後匯出的結果如下:


應該不難吧?!
這個程式會用到 檔案I/O,格式化輸出,2維陣列存取,邏輯運算子,正規式,型態轉換,
如果都能運用,基本上這個語言的基礎就都具備了。
 

程式碼:

using Printf

function to_str(data_s, s_pos, e_pos)
    str = ""
    for idx = s_pos:e_pos
        str = str*Char(data_s[idx])  #combine str
    end
    return str
end

function time_format(time_s)
    time_stamp = "20"*time_s[1:2]*"/"*time_s[3:4]*"/"*time_s[5:6]*" "*
                time_s[7:8]*":"*time_s[9:10]
end

function read_0_235(data)
    run_card = replace(to_str(data, 1, 20), r"\s" => "")
    device_name = replace(to_str(data, 21, 36), r"\s" => "")
    wafer_size = Int(Int(data[37]) * 256 + Int(data[38]) * 0.1)
    global angle = Int(data[49]) * 256 + Int(data[50])
    global col = Int(data[53]) * 256 + Int(data[54])
    global row = Int(data[55]) * 256 + Int(data[56])
    wd = replace(to_str(data, 61, 81), r"[\s+|\0+]" => "")
    lot_no = replace(to_str(data, 83, 100), r"\s" => "")
    cassette_no = Int(data[101]) * 256 + Int(data[102])
    slot_no = Int(data[103]) * 256 + Int(data[104]
    s_time = time_format(to_str(data, 149, 158))
    e_time = time_format(to_str(data, 161, 170))
    l_time = time_format(to_str(data, 173, 182))
    u_time = time_format(to_str(data, 185, 194))
    status = Int(data[209])
    global header_array = []
    push!(header_array, "Run_Card:"*run_card)
    push!(header_array, "Device_Name:"*device_name)
    push!(header_array, "Wafer_Size:"*string(wafer_size))
    push!(header_array, "Angle:"*string(angle))
    push!(header_array, "Wafer_ID:"*wd)
    push!(header_array, "Lot_No:"*lot_no)
    push!(header_array, "Cassette_No:"*string(cassette_no))
    push!(header_array, "Slot_No:"*string(slot_no))
    push!(header_array, "Start_Time :"*s_time)
    push!(header_array, "Finish_Time:"*e_time)
    push!(header_array, "Load_Time  :"*l_time)
    push!(header_array, "Unload_Time:"*u_time)
    push!(header_array, "Status:"*string(status))
end

function read_236(data)
    global map_txt = ["" for i in 1:row , j in 1:col]
    #global map_txt = ones(String, row, col)
    global pass_dies = 0
    global fail_dies = 0
    global total_dies = 0
    idx = 237
    for r = 1:row
        for c = 1:col
            die_test = Int(data[idx    ]) >> 6
            die_type = Int(data[idx + 2]) >> 6
            dummy = Int(data[idx + 2]) >> 1
            dummy = dummy & 1
            die_x = (Int(data[idx]) & 1) * 256 + Int(data[idx + 1])
            die_y = (Int(data[idx + 2]) & 1) * 256 + Int(data[idx + 3])
            bin_n = Int(data[idx + 5])
            if die_test == 0
                if (die_type == 0) && (dummy == 1)
                    map_txt[r, c] = "."
                elseif (die_type == 1) && (dummy == 0)
                    map_txt[r, c] = "M"
                elseif (die_type == 2) && (dummy == 0)
                    map_txt[r, c] = "M"
                end             
            end
            if die_test == 1  #pass die
                map_txt[r, c] = string(bin_n)
                pass_dies += 1
            end
            if die_test == 2  #fail die
                fail_dies += 1
                if bin_n == 1
                    map_txt[r, c] = string(bin_n)
                else
                    map_txt[r, c] = string(bin_n)
                end    
            end
            idx += 6
        end  #col loop end
    end  #row loop end
    total_dies = pass_dies + fail_dies
end

function print_header(array)
    for i in array
        println(i)
    end
end

function print_map()
    #map_txt2 = []
    #map_txt2 = permutedims(map_txt)
    #show(stdout, "text/plain", map_txt2)
    if angle == 90
        for i = 1:col
            for j = row:-1:1
                @printf "%2s" map_txt[j, i]
            end
            println()
        end
    end  #if end  
end

function print_summary()
    println("\n\nSummary\n===========================")
    @printf "Total Pass:%6d\n" pass_dies
    @printf "Total Fail:%6d\n" fail_dies
    @printf "Total Test:%6d\n" total_dies
end

function open_file(fname)
    println("Map File: ", fname, "\n")
    fin = open(fname, "r")
    map_data = read(fin)
end
#-----main Program-----
for i in eachindex(ARGS)
    map_data = open_file(ARGS[i])
    read_0_235(map_data)
    read_236(map_data)
    print_header(header_array)
    print_map()
    print_summary()
end  
 

上一篇:使用 Julia 讀取TSK map 檔案(一)

2019年11月6日 星期三

使用 Julia 讀取TSK map 檔案(一)

學習一種新的語言最快的方式莫過於實際的應用,TSK map 是晶片測試結果的紀錄檔,裡面
紀錄了每一個 die 的測試結果還有相關的資訊,檔案是以Binary的方式紀錄,今天就介紹用
Julia 來讀取並將內容展開。
下面是 map format 的說明書,其中map file 分為2個部分,第 0~235 byte是map header,
236~end 是每一個 die 的資訊。

首先,對 map 作讀檔的動作,將讀進的資料放入 map_data 變數中:


接著,第一筆資料是 Operator Name,是20個位元組的字元型態
剛剛講過資料是以 binary 型態儲存,因此第一筆資料的20個位元組要轉換為字元,由於後面
還會用到因此我寫一個函數來將轉換,傳回結果是字串型態:


現在就針對第一筆資料處理:
operator_name = to_str(data, 1, 20)
operator_name 字串含有空白字元因此要用正規式將空白刪掉
replace(operator_name, r"\s" => "")
因此可以簡化成:
operator_name = replace(to_str(data, 1, 20), r"\s" => "")

這裡要注意的是 Julia的陣列索引起始是 1 開始,而不是一般電腦語言是從 0 開始,
其餘只要是字元的型態都依此類推,這點讓我很不習慣啊~

第三筆資料是 Wafer Size,從第37 byte 開始佔 2 個 byte,資料型態是 binary,
wafer_size = Int(data[37]) * 256 + Int(data[38])
記得高位元組要乘上 256 + 低位元組
這樣結果會是 80(表示8吋),或是 60(6吋) 或是 120(12吋),結果再乘0.1就好
wafer_size = Int(Int(data[37]) * 256 + Int(data[38]) * 0.1)

其餘 binary 型態的資料依此類推,這樣就可以將 0~235 bytes 資料展開。
結果如下:

上圖每筆資料結尾印出"-"是為了確定字串結尾不含空白字元。

很簡單吧!

下一篇: 使用 Julia 讀取TSK map 檔案(二)